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.
This commit is contained in:
Norman Feske 2019-06-18 17:05:17 +02:00 committed by Christian Helmuth
parent 48a361107f
commit 7e7eff0eb7
2 changed files with 20 additions and 34 deletions

View File

@ -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<Main> _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) {

View File

@ -466,12 +466,13 @@ class Window_layouter::Window : public List_model<Window>::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; }