sculpt: refine keyboard entry of new depot URL

This patch includes the system dialog in the global keyboard focus
handling, supports hovering of the "Edit" and "Add" buttons,
allows the use of the enter key to finish URL editing, and
triggers a re-scan of depot users after adding a new one.

Issue #4820
This commit is contained in:
Norman Feske 2023-04-27 17:39:13 +02:00 committed by Christian Helmuth
parent 937ddd012b
commit 43d51c4499
5 changed files with 76 additions and 19 deletions

View File

@ -22,18 +22,21 @@
#include <types.h> #include <types.h>
#include <view/network_dialog.h> #include <view/network_dialog.h>
#include <view/panel_dialog.h> #include <view/panel_dialog.h>
#include <view/system_dialog.h>
namespace Sculpt { struct Keyboard_focus; } namespace Sculpt { struct Keyboard_focus; }
struct Sculpt::Keyboard_focus struct Sculpt::Keyboard_focus
{ {
enum Target { INITIAL, WPA_PASSPHRASE, WM } target { INITIAL }; enum Target { INITIAL, WPA_PASSPHRASE, SYSTEM_DIALOG, WM } target { INITIAL };
Expanding_reporter _focus_reporter; Expanding_reporter _focus_reporter;
Network_dialog const &_network_dialog; Network_dialog const &_network_dialog;
Wpa_passphrase &_wpa_passphrase; Wpa_passphrase &_wpa_passphrase;
Panel_dialog::State const &_panel; Panel_dialog::State const &_panel;
System_dialog const &_system_dialog;
bool const &_system_visible;
void update() void update()
{ {
@ -44,6 +47,9 @@ struct Sculpt::Keyboard_focus
if (_panel.network_visible() && _network_dialog.need_keyboard_focus_for_passphrase()) if (_panel.network_visible() && _network_dialog.need_keyboard_focus_for_passphrase())
target = WPA_PASSPHRASE; target = WPA_PASSPHRASE;
if (_system_dialog.keyboard_needed() && _system_visible)
target = SYSTEM_DIALOG;
if (orig_target == target) if (orig_target == target)
return; return;
@ -54,6 +60,7 @@ struct Sculpt::Keyboard_focus
switch (target) { switch (target) {
case WPA_PASSPHRASE: case WPA_PASSPHRASE:
case SYSTEM_DIALOG:
xml.attribute("label", "manager -> input"); xml.attribute("label", "manager -> input");
break; break;
@ -68,12 +75,16 @@ struct Sculpt::Keyboard_focus
Keyboard_focus(Env &env, Keyboard_focus(Env &env,
Network_dialog const &network_dialog, Network_dialog const &network_dialog,
Wpa_passphrase &wpa_passphrase, Wpa_passphrase &wpa_passphrase,
Panel_dialog::State const &panel) Panel_dialog::State const &panel,
System_dialog const &system_dialog,
bool const &system_visible)
: :
_focus_reporter(env, "focus", "focus"), _focus_reporter(env, "focus", "focus"),
_network_dialog(network_dialog), _network_dialog(network_dialog),
_wpa_passphrase(wpa_passphrase), _wpa_passphrase(wpa_passphrase),
_panel(panel) _panel(panel),
_system_dialog(system_dialog),
_system_visible(system_visible)
{ {
update(); update();
} }

View File

@ -672,7 +672,8 @@ struct Sculpt::Main : Input_event_handler,
_try_handle_click(); _try_handle_click();
} }
Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase, *this }; Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase,
*this, _system_dialog, _system_visible };
Constructible<Input::Seq_number> _clicked_seq_number { }; Constructible<Input::Seq_number> _clicked_seq_number { };
Constructible<Input::Seq_number> _clacked_seq_number { }; Constructible<Input::Seq_number> _clacked_seq_number { };
@ -784,11 +785,34 @@ struct Sculpt::Main : Input_event_handler,
} }
} }
bool _keyboard_input_consumed_by_sculpt_manager() const
{
return (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|| (_system_visible && _system_dialog.keyboard_needed());
}
struct Keyboard_focus_guard
{
Main &_main;
bool const _orig = _main._keyboard_input_consumed_by_sculpt_manager();
Keyboard_focus_guard(Main &main) : _main(main) { }
~Keyboard_focus_guard()
{
if (_orig != _main._keyboard_input_consumed_by_sculpt_manager())
_main._keyboard_focus.update();
}
};
/** /**
* Menu_view::Hover_update_handler interface * Menu_view::Hover_update_handler interface
*/ */
void menu_view_hover_updated() override void menu_view_hover_updated() override
{ {
Keyboard_focus_guard focus_guard { *this };
if (_clicked_seq_number.constructed()) if (_clicked_seq_number.constructed())
_try_handle_click(); _try_handle_click();
@ -803,6 +827,8 @@ struct Sculpt::Main : Input_event_handler,
{ {
bool need_generate_dialog = false; bool need_generate_dialog = false;
Keyboard_focus_guard focus_guard { *this };
if (ev.key_press(Input::BTN_LEFT) || ev.touch()) { if (ev.key_press(Input::BTN_LEFT) || ev.touch()) {
_clicked_seq_number.construct(_global_input_seq_number); _clicked_seq_number.construct(_global_input_seq_number);
_try_handle_click(); _try_handle_click();
@ -822,9 +848,6 @@ struct Sculpt::Main : Input_event_handler,
need_generate_dialog = true; need_generate_dialog = true;
}); });
if (ev.press())
_keyboard_focus.update();
if (need_generate_dialog) if (need_generate_dialog)
generate_dialog(); generate_dialog();
} }
@ -1710,6 +1733,7 @@ void Sculpt::Main::_handle_window_layout()
/* define window-manager focus */ /* define window-manager focus */
_wm_focus.generate([&] (Xml_generator &xml) { _wm_focus.generate([&] (Xml_generator &xml) {
_window_list.xml().for_each_sub_node("window", [&] (Xml_node win) { _window_list.xml().for_each_sub_node("window", [&] (Xml_node win) {
Label const label = win.attribute_value("label", Label()); Label const label = win.attribute_value("label", Label());
@ -1988,6 +2012,10 @@ void Sculpt::Main::_handle_runtime_state()
if (_index_update_queue.download_count != orig_download_count) if (_index_update_queue.download_count != orig_download_count)
_deploy.update_installation(); _deploy.update_installation();
/* update depot-user selection after adding new depot URL */
if (_system_visible)
trigger_depot_query();
/* /*
* The removal of an index file may have completed, re-query index * The removal of an index file may have completed, re-query index
* files to reflect this change at the depot selection menu. * files to reflect this change at the depot selection menu.

View File

@ -14,6 +14,7 @@
#ifndef _MODEL__DOWNLOAD_QUEUE_H_ #ifndef _MODEL__DOWNLOAD_QUEUE_H_
#define _MODEL__DOWNLOAD_QUEUE_H_ #define _MODEL__DOWNLOAD_QUEUE_H_
#include <depot/archive.h>
#include <base/registry.h> #include <base/registry.h>
#include <types.h> #include <types.h>

View File

@ -19,6 +19,7 @@
#define _MODEL__INDEX_UPDATE_QUEUE_H_ #define _MODEL__INDEX_UPDATE_QUEUE_H_
#include <model/download_queue.h> #include <model/download_queue.h>
#include <model/file_operation_queue.h>
#include <types.h> #include <types.h>
namespace Sculpt { struct Index_update_queue; } namespace Sculpt { struct Index_update_queue; }

View File

@ -49,6 +49,8 @@ struct Sculpt::Depot_users_dialog
bool _unfolded = false; bool _unfolded = false;
bool _selected_user_exists = false;
Hoverable_item _user { }; Hoverable_item _user { };
Hoverable_item _button { }; Hoverable_item _button { };
@ -124,8 +126,9 @@ struct Sculpt::Depot_users_dialog
bool const selected = (name == _selected); bool const selected = (name == _selected);
Url const url = _url(user); Url const url = _url(user);
Url const label = Depot_url::from_string(url).valid() ? url : Url(name); Url const label = Depot_url::from_string(url).valid() ? url : Url(name);
bool const show_all = _unfolded || !_selected_user_exists;
if (!selected && !_unfolded) if (!selected && !show_all)
return; return;
_gen_item(xml, name, _gen_item(xml, name,
@ -133,7 +136,7 @@ struct Sculpt::Depot_users_dialog
[&] /* right */ { } [&] /* right */ { }
); );
if (_unfolded && !last) if (show_all && !last)
_gen_vspacer(xml, String<64>("below ", name).string()); _gen_vspacer(xml, String<64>("below ", name).string());
} }
@ -173,6 +176,9 @@ struct Sculpt::Depot_users_dialog
gen_named_node(xml, "button", "add", [&] { gen_named_node(xml, "button", "add", [&] {
if (!url_valid) if (!url_valid)
xml.attribute("style", "unimportant"); xml.attribute("style", "unimportant");
else
_button.gen_hovered_attr(xml, "add");
xml.node("label", [&] { xml.node("label", [&] {
if (!url_valid) if (!url_valid)
xml.attribute("style", "unimportant"); xml.attribute("style", "unimportant");
@ -180,6 +186,7 @@ struct Sculpt::Depot_users_dialog
}); });
} else { } else {
gen_named_node(xml, "button", "edit", [&] { gen_named_node(xml, "button", "edit", [&] {
_button.gen_hovered_attr(xml, "edit");
xml.node("label", [&] { xml.node("label", [&] {
xml.attribute("text", "Edit"); }); }); xml.attribute("text", "Edit"); }); });
} }
@ -208,12 +215,12 @@ struct Sculpt::Depot_users_dialog
bool const last = (--remain_count == 0); bool const last = (--remain_count == 0);
_gen_entry(xml, user, last); }); _gen_entry(xml, user, last); });
if (_unfolded) if (_unfolded || !_selected_user_exists)
_gen_add_entry(xml, depot_users); _gen_add_entry(xml, depot_users);
}); });
}); });
if (!_unfolded && !known_pubkey) { if (!_unfolded && !known_pubkey && _selected_user_exists) {
gen_named_node(xml, "button", "pubkey warning", [&] { gen_named_node(xml, "button", "pubkey warning", [&] {
xml.attribute("style", "invisible"); xml.attribute("style", "invisible");
xml.node("label", [&] { xml.node("label", [&] {
@ -240,7 +247,7 @@ struct Sculpt::Depot_users_dialog
void generate(Xml_generator &xml) const { _gen_selection(xml); } void generate(Xml_generator &xml) const { _gen_selection(xml); }
bool unfolded() const { return _unfolded; } bool unfolded() const { return _unfolded || !_selected_user_exists; }
struct User_properties struct User_properties
{ {
@ -280,6 +287,7 @@ struct Sculpt::Depot_users_dialog
_selected = user; _selected = user;
select_fn(user); select_fn(user);
_unfolded = false; _unfolded = false;
_selected_user_exists = true;
_url_edit_field = _orig_edit_url; _url_edit_field = _orig_edit_url;
}; };
@ -324,6 +332,19 @@ struct Sculpt::Depot_users_dialog
if (c.value == ' ' || c.value == '"') if (c.value == ' ' || c.value == '"')
return; return;
/* respond to enter key */
if (c.value == 10) {
Depot_url const depot_url = _depot_url(_depot_users.xml());
if (depot_url.valid()) {
_action.add_depot_url(depot_url);
_selected = depot_url.user;
_selected_user_exists = true;
_unfolded = false;
_url_edit_field = _orig_edit_url;
}
return;
}
_url_edit_field.apply(c); _url_edit_field.apply(c);
} }
@ -335,16 +356,11 @@ struct Sculpt::Depot_users_dialog
* If the selected depot user does not exist in the depot, show * If the selected depot user does not exist in the depot, show
* list of available users. * list of available users.
*/ */
bool selected_user_exists = false; _selected_user_exists = false;
_depot_users.xml().for_each_sub_node([&] (Xml_node const &user) { _depot_users.xml().for_each_sub_node([&] (Xml_node const &user) {
if (_selected == user.attribute_value("name", User())) if (_selected == user.attribute_value("name", User()))
selected_user_exists = true; }); _selected_user_exists = true; });
if (!selected_user_exists) {
_selected = _default_user;
_unfolded = true;
}
} }
}; };