sculpt_manager: touch control of popup dialog

When trying to control the popup dialog with touch events, the dialog
was immediately closed (on any touch). This was a consequence of
evaluating the dialog's hover state without waiting for the
corresponding hover report. For regular motiong events, the hover report
is updated when the pointer moves. For touch events, however, the
hover report is only updated when the touch occurs. We therefore need to
wait for the hover report that corresponds to the touch event before
deciding about closing the popup dialog.

Since menu_view's hover report is not updated for clicks/touches outside any
dialog, we are now using nitpicker's hover report, which also got
augmented by the sequence number in order to be correlated with the
click/touch event.

Fixes #5485
This commit is contained in:
Johannes Schlatow 2025-03-05 15:14:58 +01:00 committed by Norman Feske
parent 1336495858
commit 9e2e592283
6 changed files with 67 additions and 18 deletions

View File

@ -87,6 +87,7 @@
<service name="Gui"/> <service name="Capture"/> <service name="Event"/>
</provides>
<config focus="rom" request_input="yes" request_framebuffer="yes">
<report hover="yes"/>
<background color="#272f45"/>
<domain name="default" layer="1" content="client" label="no" hover="always" focus="click"/>
<default-policy domain="default"/>
@ -116,6 +117,7 @@
<policy label="manager -> decorator_margins" report="decorator -> decorator_margins"/>
<policy label="nitpicker -> focus" report="manager -> focus"/>
<policy label="gui_fb -> config" report="manager -> gui_fb_config"/>
<policy label="manager -> hover" report="nitpicker -> hover"/>
<policy label="runtime -> leitzentrale -> diag_dialog"
report="manager -> diag_dialog"/>
@ -133,7 +135,7 @@
report="manager -> panel_dialog"/>
<policy label="runtime -> leitzentrale -> runtime_dialog"
report="manager -> runtime_dialog"/>
<policy label="manager -> hover"
<policy label="manager -> menu_hover"
report="runtime -> leitzentrale -> runtime_view -> hover"/>
</config>
</start>
@ -240,6 +242,7 @@
<service name="ROM" label="nitpicker_focus"> <parent/> </service>
<service name="ROM" label="nitpicker_hover"> <parent/> </service>
<service name="ROM" label="hover"> <child name="report_rom"/> </service>
<service name="ROM" label="menu_hover"> <child name="report_rom"/> </service>
<service name="ROM" label="window_list"> <child name="report_rom"/> </service>
<service name="ROM" label="decorator_margins"> <child name="report_rom"/> </service>
<service name="Gui"> <parent/> </service>

View File

@ -63,7 +63,7 @@ class Dialog::Distant_runtime : Noncopyable
Top_level_dialog::Name _hovered_dialog { };
Sculpt::Rom_handler<Distant_runtime> _hover_rom {
_env, "hover", *this, &Distant_runtime::_handle_hover };
_env, "menu_hover", *this, &Distant_runtime::_handle_hover };
void _handle_hover(Xml_node const &);

View File

@ -762,12 +762,35 @@ struct Sculpt::Main : Input_event_handler,
Rom_handler<Main> _nitpicker_hover_handler {
_env, "nitpicker_hover", *this, &Main::_handle_nitpicker_hover };
Rom_handler<Main> _hover_handler {
_env, "hover", *this, &Main::_handle_hover };
Expanding_reporter _gui_fb_config { _env, "config", "gui_fb_config" };
Constructible<Gui::Point> _pointer_pos { };
Fb_connectors::Name _hovered_display { };
void _handle_hover(Xml_node const &hover)
{
using Label = String<128>;
Label label { hover.attribute_value("label", Label()) };
Label suffix { "popup_dialog" };
_popup_hovered = false;
if (label.length() >= suffix.length()) {
size_t const offset = label.length() - suffix.length();
if (!strcmp(label.string() + offset, suffix.string()))
_popup_hovered = true;
}
_hover_seq_number = { hover.attribute_value("seq_number", 0U) };
_try_handle_click();
}
void _handle_nitpicker_hover(Xml_node const &hover)
{
if (hover.has_attribute("xpos"))
@ -942,7 +965,11 @@ struct Sculpt::Main : Input_event_handler,
/* used to prevent closing the popup immediatedly after opened */
Input::Seq_number _popup_opened_seq_number { };
/* used to correlate clicks and hover reports */
Input::Seq_number _hover_seq_number { };
Input::Seq_number _clicked_seq_number { };
Input::Seq_number _last_clicked_seq_number { };
bool _popup_hovered { false };
/**
* Input_event_handler interface
@ -962,23 +989,11 @@ struct Sculpt::Main : Input_event_handler,
* Detect clicks outside the popup dialog (for closing it)
*/
if (ev.key_press(Input::BTN_LEFT) || ev.touch()) {
_clicked_seq_number = _global_input_seq_number;
bool const popup_opened =
(_popup_opened_seq_number.value == _clicked_seq_number.value);
bool const popup_hovered =
_popup_dialog.if_hovered([&] (Hovered_at const &) { return true; });
if (!popup_hovered && !popup_opened) {
if (_popup.state == Popup::VISIBLE) {
_close_popup_dialog();
discard_construction();
}
}
_try_handle_click();
}
bool need_generate_dialog = false;
ev.handle_press([&] (Input::Keycode, Codepoint code) {
@ -1156,6 +1171,27 @@ struct Sculpt::Main : Input_event_handler,
_update_window_layout();
}
void _try_handle_click()
{
/* skip if already handled */
if (_last_clicked_seq_number.value == _clicked_seq_number.value)
return;
/* wait for hover to be updated */
if (_clicked_seq_number.value != _hover_seq_number.value)
return;
_last_clicked_seq_number = _clicked_seq_number;
bool const popup_opened =
(_popup_opened_seq_number.value == _clicked_seq_number.value);
if ((_popup.state == Popup::VISIBLE) && !_popup_hovered && !popup_opened) {
_close_popup_dialog();
discard_construction();
}
}
void _refresh_panel_and_window_layout()
{
_panel_dialog.refresh();

View File

@ -960,7 +960,7 @@ void Nitpicker::Main::handle_input_events(User_state::Input_batch batch)
_view_stack.update_all_views();
}
if (result.hover_changed)
if (result.hover_changed || result.last_seq_changed)
_hover_count++;
/* report mouse-position updates */

View File

@ -342,6 +342,8 @@ User_state::handle_input_events(Input_batch batch)
View_owner const * const old_input_receiver = _input_receiver;
View_owner const * const old_last_clicked = _last_clicked;
unsigned const old_clicked_count = _clicked_count;
unsigned const old_seq_number = _last_seq_number.constructed()
? _last_seq_number->value : 0;
bool button_activity = false;
@ -424,6 +426,9 @@ User_state::handle_input_events(Input_batch batch)
[&] (Nowhere) { return _pointer.ok(); });
};
bool const last_seq_changed = _last_seq_number.constructed()
&& _last_seq_number->value != old_seq_number;
return {
.hover_changed = _hovered != old_hovered,
.focus_changed = (_focused != old_focused) ||
@ -432,7 +437,8 @@ User_state::handle_input_events(Input_batch batch)
.button_activity = button_activity,
.motion_activity = pointer_changed() || touch_occurred,
.key_pressed = _key_pressed(),
.last_clicked_changed = last_clicked_changed
.last_clicked_changed = last_clicked_changed,
.last_seq_changed = last_seq_changed
};
}
@ -460,6 +466,9 @@ void User_state::report_hovered_view_owner(Xml_generator &xml, bool active) cons
_hovered->report(xml);
if (active) xml.attribute("active", "yes");
if (_last_seq_number.constructed())
xml.attribute("seq_number", _last_seq_number->value);
}

View File

@ -252,6 +252,7 @@ class Nitpicker::User_state
bool const motion_activity;
bool const key_pressed;
bool const last_clicked_changed;
bool const last_seq_changed;
};
Handle_input_result handle_input_events(Input_batch);