Skip to content

Commit

Permalink
overhauled how output change works and also added a toggelable option…
Browse files Browse the repository at this point in the history
… for ignoring or respecting workspace bounds (before it would always ignore it)
  • Loading branch information
hazelwiss committed Dec 28, 2023
1 parent 9acf7e7 commit 1240128
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 34 deletions.
5 changes: 5 additions & 0 deletions metadata/focus-change.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
<_long>enables moving focus between different outputs</_long>
<default>false</default>
</option>
<option name="cross-workspace" type="bool">
<_short>ignores workspace boundraries</_short>
<_long>enables moving focus between workspaces on the same output</_long>
<default>false</default>
</option>
<option name="raise-on-change" type="bool">
<_short>raise window on change</_short>
<_long>raise the newly focused window to the top</_long>
Expand Down
170 changes: 136 additions & 34 deletions src/focus-change.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <algorithm>
#include <cstdint>
#include <iterator>
#include <wayfire/bindings.hpp>
Expand All @@ -30,6 +31,7 @@
#include <wayfire/option-wrapper.hpp>
#include <wayfire/config/types.hpp>
#include <wayfire/seat.hpp>
#include <wayfire/toplevel-view.hpp>
#include <wayfire/view.hpp>
#include <wayfire/workspace-set.hpp>
#include <wayfire/bindings-repository.hpp>
Expand Down Expand Up @@ -57,51 +59,46 @@ class wayfire_focus_change_t : public wf::plugin_interface_t
grace_right{"focus-change/grace-right"},
grace_left{"focus-change/grace-left"};
wf::option_wrapper_t<bool> cross_outputs{"focus-change/cross-output"};
wf::option_wrapper_t<bool> cross_workspace{"focus-change/cross-workspace"};
wf::option_wrapper_t<bool> raise_on_change{"focus-change/raise-on-change"};
wf::option_wrapper_t<int> scan_height{"focus-change/scan-height"},
scan_width{"focus-change/scan-width"};


void change_focus(orientation_t orientation)
{
const bool should_pad_workspace = cross_outputs.value();
const auto cur_view = wf::get_core().seat->get_active_view();
const auto cur_output = cur_view->get_output();
const auto cur_lg = cur_output->get_layout_geometry();
const auto cur_bb = cur_view->get_bounding_box();
const int32_t cur_cx = cur_bb.x + cur_bb.width / 2 + (should_pad_workspace ? cur_lg.x : 0);
const int32_t cur_cy = cur_bb.y + cur_bb.height / 2 + (should_pad_workspace ? cur_lg.y : 0);
wf::view_interface_t *new_focus = nullptr;
const int32_t cur_cx = cur_bb.x + cur_bb.width / 2;
const int32_t cur_cy = cur_bb.y + cur_bb.height / 2;
wf::toplevel_view_interface_t *new_focus = nullptr;

auto iterating_output = std::vector<wayfire_toplevel_view>{};
if (cross_outputs.value())
{
auto outputs = wf::get_core().output_layout->get_outputs();
for (auto output : std::move(outputs))
{
auto vec = output->wset()->get_views();
iterating_output.insert(
iterating_output.end(),
std::make_move_iterator(vec.begin()),
std::make_move_iterator(vec.end()));
}
} else
{
iterating_output = cur_output->wset()->get_views();
}
const bool cross_ws = cross_workspace.value();
const auto workspace =
cross_ws ? std::optional<wf::point_t>{} : std::optional{cur_output->wset()->
get_current_workspace()};

int32_t closest_cur = INT32_MAX;
auto iterating_output = cur_output->wset()->get_views(0, workspace);
int32_t closest_cur = INT32_MAX;
for (auto&& view : std::move(iterating_output))
{
if (view->get_id() == cur_view->get_id())
{
continue;
}

const auto bb = view->get_bounding_box();
const auto lg = view->get_output()->get_layout_geometry();
const int32_t cx = bb.x + bb.width / 2 + (should_pad_workspace ? lg.x : 0);
const int32_t cy = bb.y + bb.height / 2 + (should_pad_workspace ? lg.y : 0);
const auto bb = view->get_bounding_box();
const auto lg = view->get_output()->get_layout_geometry();
const int32_t cxr = bb.width / 2;
const int32_t cyr = bb.height / 2;
const int32_t cx = bb.x + cxr;
const int32_t cy = bb.y + cyr;

if (!cross_ws && ((cx < 0) || (cx >= lg.width) || (cy < 0) || (cy >= lg.height)))
{
continue;
}

const int32_t scan_w_intrm = scan_width.value() > 0 ?
scan_width.value() : scan_width.value() < 0 ?
Expand All @@ -124,11 +121,11 @@ class wayfire_focus_change_t : public wf::plugin_interface_t
const int32_t bias_right = grace_right.value();
const int32_t bias_left = grace_left.value();

const bool w_cond = cx >= scan_w_l && cx <= scan_w_h;
const bool h_cond = cy >= scan_h_l && cy <= scan_h_h;
const bool w_cond = cx + cxr >= scan_w_l && cx - cxr <= scan_w_h;
const bool h_cond = cy + cyr >= scan_h_l && cy - cyr <= scan_h_h;

bool contains = false;
int32_t distance = 0;
int32_t distance = INT32_MAX;
switch (orientation)
{
case orientation_t::UP:
Expand All @@ -155,19 +152,124 @@ class wayfire_focus_change_t : public wf::plugin_interface_t
;
}

if ((distance >= 0) && contains)
if ((distance >= 0) && contains && (distance < closest_cur))
{
closest_cur = distance;
new_focus = view.get();
}
}

if ((new_focus == nullptr) && cross_outputs.value())
{
const auto pos = cur_output->get_layout_geometry();

auto outputs = wf::get_core().output_layout->get_outputs();
wf::output_t *output = nullptr;
int32_t closest_output = INT32_MAX;
for (auto op : outputs)
{
if (op->get_id() == cur_output->get_id())
{
continue;
}

const auto cmp_pos = op->get_layout_geometry();

const int32_t cx = cmp_pos.x + cmp_pos.width / 2;
const int32_t cy = cmp_pos.y + cmp_pos.height / 2;

const bool align_x = cx >= pos.x && cx < pos.x + pos.width;
const bool align_y = cy >= pos.y && cy < pos.y + pos.height;

int32_t distance = INT32_MAX;
bool cond = false;
switch (orientation)
{
case orientation_t::UP:
cond = align_x;
distance = pos.y - cmp_pos.y;
break;

case orientation_t::DOWN:
cond = align_x;
distance = cmp_pos.y - pos.y;
break;

case orientation_t::RIGHT:
cond = align_y;
distance = cmp_pos.x - pos.x;
break;

case orientation_t::LEFT:
cond = align_y;
distance = pos.x - cmp_pos.x;
break;

default: /* unreachable */
;
}

if (cond && (distance > 0) && (distance < closest_output))
{
closest_output = distance;
output = op;
}
}

if ((output != nullptr))
{
if (distance < closest_cur)
const auto cur_ws = output->wset()->get_current_workspace();

auto views = output->wset()->get_views(0, std::optional{cur_ws});
int32_t closest_cur = INT32_MAX;
for (auto&& view : std::move(views))
{
closest_cur = distance;
new_focus = view.get();
const auto bb = view.get()->get_bounding_box();
const auto cx = bb.x + bb.width / 2;
const auto cy = bb.y + bb.height / 2;
if ((cx < 0) || (cx >= pos.width) || (cy < 0) || (cy >= pos.height))
{
continue;
}

int32_t distance = INT32_MAX;
switch (orientation)
{
case orientation_t::UP:
distance = cy;
break;

case orientation_t::DOWN:
distance = pos.height - cy;
break;

case orientation_t::RIGHT:
distance = cx;
break;

case orientation_t::LEFT:
distance = pos.width - cx;
break;

default: /* unreachable */
;
}

if ((distance >= 0) && (distance < closest_cur))
{
closest_cur = distance;
new_focus = view.get();
}
}
}
}

if (new_focus != nullptr)
{
wf::get_core().seat->focus_output(new_focus->get_output());
auto op = new_focus->get_output();
const auto ws = op->wset()->get_view_main_workspace(new_focus);
op->wset()->set_workspace(ws);
wf::get_core().seat->focus_output(op);
wf::get_core().seat->focus_view(new_focus->self());
if (raise_on_change.value())
{
Expand Down

0 comments on commit 1240128

Please sign in to comment.