From 7e7eff0eb72b477a8a859f14ac640c51c5b0d6ab Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 18 Jun 2019 17:05:17 +0200 Subject: [PATCH] window layouter: reduce snap-back artifact When resizing windows of clients that respond very slowly to resize requests, the window's size sometimes snapped back to its original size immediately after finishing the drag operation. The problem was caused by the interplay of the layout rules (obtained via the 'rules' ROM, generated by the 'rules' report) and the temporary interactive state that occurs during drag operations. The rules are updated only at the time of releasing the button to keep the overhead while dragging the window low. However, when releasing the mouse, the (now outdated) rules kicked back into effect, triggering resize requests for the window to its old size. The patch solves this problem by decoupling the dragged state of a window from the physical release of the button. The button release triggers a transition from a DRAGGING to a SETTLING state and programs a timer. In the SETTLING state, the windows behave as in DRAGGING state, giving the interactive geometry precedence over the rules-dictated geometry. During this state, further responses of window-resize requests may come in and are handled like dragging was still in progress. After a timeout, however, the current window layout is conserved as a new rules report and the state goes back to IDLE. For clients that takes a very long time (in particular, VirtualBox when resizing the desktop, which takes sometimes multiple seconds), the snap-back artifact can still occur, but the effect is reduced. --- repos/gems/src/app/window_layouter/main.cc | 49 +++++++-------------- repos/gems/src/app/window_layouter/window.h | 5 ++- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/repos/gems/src/app/window_layouter/main.cc b/repos/gems/src/app/window_layouter/main.cc index 66ed892998..c92d6e355d 100644 --- a/repos/gems/src/app/window_layouter/main.cc +++ b/repos/gems/src/app/window_layouter/main.cc @@ -48,7 +48,9 @@ struct Window_layouter::Main : Operations, Timer::Connection _drop_timer { _env }; - bool _defer_layout_change = false; + enum class Drag_state { IDLE, DRAGGING, SETTLING }; + + Drag_state _drag_state { Drag_state::IDLE }; Signal_handler
_drop_timer_handler { _env.ep(), *this, &Main::_handle_drop_timer }; @@ -138,21 +140,9 @@ struct Window_layouter::Main : Operations, */ void layout_rules_changed() override { - /* - * When re-importing rules generated by the drop operation, issue an - * updated resize request immediately instead of waiting for the - * '_drop_timer_handler'. Clear '_defer_layout_change' before calling - * '_update_window_layout' because '_gen_window_layout' evaluates - * this flag. - */ - bool const issue_resize_request = _defer_layout_change; - - _defer_layout_change = false; - _update_window_layout(); - if (issue_resize_request) - _gen_resize_request(); + _gen_resize_request(); } /** @@ -220,6 +210,11 @@ struct Window_layouter::Main : Operations, void drag(Window_id id, Window::Element element, Point clicked, Point curr) override { + if (_drag_state == Drag_state::SETTLING) + return; + + _drag_state = Drag_state::DRAGGING; + to_front(id); bool window_layout_changed = false; @@ -245,11 +240,15 @@ struct Window_layouter::Main : Operations, void _handle_drop_timer() { - /* discharge '_defer_layout_change' */ - layout_rules_changed(); + _drag_state = Drag_state::IDLE; + + _gen_rules(); + + _window_list.for_each_window([&] (Window &window) { + window.finalize_drag_operation(); }); } - void finalize_drag(Window_id id, Window::Element, Point, Point) override + void finalize_drag(Window_id, Window::Element, Point, Point) override { /* * Update window layout because highlighting may have changed after the @@ -257,17 +256,9 @@ struct Window_layouter::Main : Operations, * dragging of a resize handle, the resize handle is no longer hovered. */ _gen_window_layout(); - _gen_rules(); - _window_list.with_window(id, [&] (Window &window) { - window.finalize_drag_operation(); }); + _drag_state = Drag_state::SETTLING; - /* - * Mask the generation of resize requests until the updated rules are - * imported the next time. Discharge the masking after a timeout for - * the case where rules fixed and not fed back to the layouter. - */ - _defer_layout_change = true; _drop_timer.trigger_once(250*1000); } @@ -400,9 +391,6 @@ struct Window_layouter::Main : Operations, void Window_layouter::Main::_gen_window_layout() { - if (_defer_layout_change) - return; - /* update hover and focus state of each window */ _window_list.for_each_window([&] (Window &window) { @@ -420,9 +408,6 @@ void Window_layouter::Main::_gen_window_layout() void Window_layouter::Main::_gen_resize_request() { - if (_defer_layout_change) - return; - bool resize_needed = false; _assign_list.for_each([&] (Assign const &assign) { assign.for_each_member([&] (Assign::Member const &member) { diff --git a/repos/gems/src/app/window_layouter/window.h b/repos/gems/src/app/window_layouter/window.h index bab593ca04..957046995b 100644 --- a/repos/gems/src/app/window_layouter/window.h +++ b/repos/gems/src/app/window_layouter/window.h @@ -466,12 +466,13 @@ class Window_layouter::Window : public List_model::Element void finalize_drag_operation() { - _dragged = false; _drag_left_border = false; _drag_right_border = false; _drag_top_border = false; _drag_bottom_border = false; - _dragged_size = effective_inner_geometry().area(); + _geometry = effective_inner_geometry(); + _dragged_size = _geometry.area(); + _dragged = false; } void to_front_cnt(unsigned to_front_cnt) { _to_front_cnt = to_front_cnt; }