mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
parent
1490c58f8b
commit
0a71c8f3e1
@ -72,6 +72,7 @@ static inline bool Sculpt::blueprint_rom_missing(Xml_node blueprint, Path const
|
||||
pkg.for_each_sub_node("missing_rom", [&] (Xml_node missing_rom) {
|
||||
|
||||
/* ld.lib.so is always taken from the base system */
|
||||
using Label = String<64>;
|
||||
Label const label = missing_rom.attribute_value("label", Label());
|
||||
if (label == "ld.lib.so")
|
||||
return;
|
||||
|
@ -125,8 +125,7 @@ class Dialog::Distant_runtime::View : private Views::Element
|
||||
|
||||
Event::Seq_number _hover_seq_number { };
|
||||
|
||||
template <typename FN>
|
||||
void _with_dialog_hover(FN const &fn) const
|
||||
void _with_dialog_hover(auto const &fn) const
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
@ -254,6 +253,15 @@ class Dialog::Distant_runtime::View : private Views::Element
|
||||
{ }
|
||||
|
||||
void refresh() { _refresh_handler.local_submit(); }
|
||||
|
||||
bool if_hovered(auto const &fn) const
|
||||
{
|
||||
bool result = false;
|
||||
if (_dialog_hovered)
|
||||
_with_dialog_hover([&] (Xml_node const &location) {
|
||||
result = fn(Hovered_at { Event::Seq_number { }, location }); });
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _DIALOG__DISTANT_RUNTIME_H_ */
|
||||
|
@ -29,9 +29,6 @@ struct Sculpt::Feature
|
||||
|
||||
/* allow the browsing of file systems via the inspect view */
|
||||
static constexpr bool INSPECT_VIEW = true;
|
||||
|
||||
/* highlight hovered buttons, not possible on touch-screen devices */
|
||||
static constexpr bool VISUAL_HOVER = true;
|
||||
};
|
||||
|
||||
#endif /* _FEATURE_H_ */
|
||||
|
@ -70,7 +70,7 @@ struct Dialog::Selectable_node
|
||||
if (s.hovered()) s.attribute("hovered", "yes");
|
||||
if (attr.selected) s.attribute("selected", "yes");
|
||||
|
||||
s.sub_scope<Dialog::Label>(attr.pretty_name);
|
||||
s.sub_scope<Label>(attr.pretty_name);
|
||||
});
|
||||
|
||||
if (attr.selected)
|
||||
@ -101,7 +101,7 @@ void Graph::_view_selected_node_content(Scope<Depgraph, Frame, Vbox> &s,
|
||||
}
|
||||
|
||||
if (name == "ram_fs")
|
||||
s.widget(_ram_fs_dialog, _sculpt_partition, _ram_fs_state);
|
||||
s.widget(_ram_fs_widget, _sculpt_partition, _ram_fs_state);
|
||||
|
||||
String<100> const
|
||||
ram (Capacity{info.assigned_ram - info.avail_ram}, " / ",
|
||||
@ -110,8 +110,8 @@ void Graph::_view_selected_node_content(Scope<Depgraph, Frame, Vbox> &s,
|
||||
info.assigned_caps, " caps");
|
||||
|
||||
s.sub_scope<Min_ex>(25);
|
||||
s.sub_scope<Dialog::Label>(ram);
|
||||
s.sub_scope<Dialog::Label>(caps);
|
||||
s.sub_scope<Label>(ram);
|
||||
s.sub_scope<Label>(caps);
|
||||
}
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ void Graph::_view_storage_node(Scope<Depgraph> &s) const
|
||||
},
|
||||
[&] (Scope<Depgraph, Frame, Vbox> &s) {
|
||||
s.sub_scope<Frame>([&] (Scope<Depgraph, Frame, Vbox, Frame> &s) {
|
||||
s.widget(_block_devices_dialog); });
|
||||
s.widget(_block_devices_widget); });
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -147,7 +147,7 @@ void Graph::_view_usb_node(Scope<Depgraph> &s) const
|
||||
},
|
||||
[&] (Scope<Depgraph, Frame, Vbox> &s) {
|
||||
s.sub_scope<Frame>([&] (Scope<Depgraph, Frame, Vbox, Frame> &s) {
|
||||
s.widget(_usb_devices_dialog); });
|
||||
s.widget(_usb_devices_widget); });
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -283,8 +283,8 @@ void Graph::click(Clicked_at const &at, Action &action)
|
||||
_storage_selected = !_storage_selected && (id.value == "storage");
|
||||
_usb_selected = !_usb_selected && (id.value == "usb");
|
||||
|
||||
if (_usb_selected) _usb_devices_dialog .reset();
|
||||
if (_storage_selected) _block_devices_dialog.reset();
|
||||
if (_usb_selected) _usb_devices_widget .reset();
|
||||
if (_storage_selected) _block_devices_widget.reset();
|
||||
|
||||
_runtime_config.with_start_name(id, [&] (Start_name const &name) {
|
||||
_runtime_state.toggle_selection(name, _runtime_config); });
|
||||
@ -307,20 +307,20 @@ void Graph::click(Clicked_at const &at, Action &action)
|
||||
action.open_popup_dialog(popup_anchor(at._location));
|
||||
});
|
||||
|
||||
_ram_fs_dialog .propagate(at, _sculpt_partition, action);
|
||||
_block_devices_dialog.propagate(at, action);
|
||||
_usb_devices_dialog .propagate(at, action);
|
||||
_ram_fs_widget .propagate(at, _sculpt_partition, action);
|
||||
_block_devices_widget.propagate(at, action);
|
||||
_usb_devices_widget .propagate(at, action);
|
||||
|
||||
_remove .propagate(at);
|
||||
_restart.propagate(at);
|
||||
}
|
||||
|
||||
|
||||
void Graph::clack(Clacked_at const &at, Action &action, Ram_fs_dialog::Action &ram_fs_action)
|
||||
void Graph::clack(Clacked_at const &at, Action &action, Ram_fs_widget::Action &ram_fs_action)
|
||||
{
|
||||
_ram_fs_dialog .propagate(at, ram_fs_action);
|
||||
_block_devices_dialog.propagate(at, action);
|
||||
_usb_devices_dialog .propagate(at, action);
|
||||
_ram_fs_widget .propagate(at, ram_fs_action);
|
||||
_block_devices_widget.propagate(at, action);
|
||||
_usb_devices_widget .propagate(at, action);
|
||||
|
||||
_remove.propagate(at, [&] {
|
||||
action.remove_deployed_component(_runtime_state.selected());
|
||||
|
@ -23,10 +23,8 @@
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <xml.h>
|
||||
#include <view/activatable_item.h>
|
||||
#include <view/storage_dialog.h>
|
||||
#include <view/ram_fs_dialog.h>
|
||||
#include <view/storage_widget.h>
|
||||
#include <view/ram_fs_widget.h>
|
||||
#include <model/capacity.h>
|
||||
#include <model/popup.h>
|
||||
#include <model/runtime_config.h>
|
||||
@ -49,19 +47,19 @@ struct Sculpt::Graph : Widget<Depgraph>
|
||||
|
||||
Hosted<Depgraph, Toggle_button> _plus { Id { "+" } };
|
||||
|
||||
Hosted<Depgraph, Frame, Vbox, Ram_fs_dialog>
|
||||
_ram_fs_dialog { Id { "ram_fs_dialog" } };
|
||||
Hosted<Depgraph, Frame, Vbox, Ram_fs_widget>
|
||||
_ram_fs_widget { Id { "ram_fs" } };
|
||||
|
||||
Hosted<Depgraph, Frame, Vbox, Frame, Hbox, Deferred_action_button>
|
||||
_remove { Id { "Remove" } },
|
||||
_restart { Id { "Restart" } };
|
||||
|
||||
Hosted<Depgraph, Frame, Vbox, Frame, Block_devices_dialog>
|
||||
_block_devices_dialog { Id { "block_devices" },
|
||||
Hosted<Depgraph, Frame, Vbox, Frame, Block_devices_widget>
|
||||
_block_devices_widget { Id { "block_devices" },
|
||||
_storage_devices, _sculpt_partition };
|
||||
|
||||
Hosted<Depgraph, Frame, Vbox, Frame, Usb_devices_dialog>
|
||||
_usb_devices_dialog { Id { "usb_devices" },
|
||||
Hosted<Depgraph, Frame, Vbox, Frame, Usb_devices_widget>
|
||||
_usb_devices_widget { Id { "usb_devices" },
|
||||
_storage_devices, _sculpt_partition };
|
||||
|
||||
bool _storage_selected = false;
|
||||
@ -90,7 +88,7 @@ struct Sculpt::Graph : Widget<Depgraph>
|
||||
|
||||
void view(Scope<Depgraph> &) const;
|
||||
|
||||
struct Action : Storage_device_dialog::Action
|
||||
struct Action : Storage_device_widget::Action
|
||||
{
|
||||
virtual void remove_deployed_component(Start_name const &) = 0;
|
||||
virtual void restart_deployed_component(Start_name const &) = 0;
|
||||
@ -98,12 +96,12 @@ struct Sculpt::Graph : Widget<Depgraph>
|
||||
};
|
||||
|
||||
void click(Clicked_at const &, Action &);
|
||||
void clack(Clacked_at const &, Action &, Ram_fs_dialog::Action &);
|
||||
void clack(Clacked_at const &, Action &, Ram_fs_widget::Action &);
|
||||
|
||||
void reset_storage_operation()
|
||||
{
|
||||
_block_devices_dialog.reset_operation();
|
||||
_usb_devices_dialog.reset_operation();
|
||||
_block_devices_widget.reset_operation();
|
||||
_usb_devices_widget.reset_operation();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
/* local includes */
|
||||
#include <model/wpa_passphrase.h>
|
||||
#include <types.h>
|
||||
#include <view/network_dialog.h>
|
||||
#include <view/network_widget.h>
|
||||
#include <view/panel_dialog.h>
|
||||
#include <view/system_dialog.h>
|
||||
|
||||
@ -32,7 +32,7 @@ struct Sculpt::Keyboard_focus
|
||||
|
||||
Expanding_reporter _focus_reporter;
|
||||
|
||||
Network_dialog const &_network_dialog;
|
||||
Network_widget const &_network_widget;
|
||||
Wpa_passphrase &_wpa_passphrase;
|
||||
Panel_dialog::State const &_panel;
|
||||
System_dialog const &_system_dialog;
|
||||
@ -44,7 +44,7 @@ struct Sculpt::Keyboard_focus
|
||||
|
||||
target = WM;
|
||||
|
||||
if (_panel.network_visible() && _network_dialog.need_keyboard_focus_for_passphrase())
|
||||
if (_panel.network_visible() && _network_widget.need_keyboard_focus_for_passphrase())
|
||||
target = WPA_PASSPHRASE;
|
||||
|
||||
if (_system_dialog.keyboard_needed() && _system_visible)
|
||||
@ -73,14 +73,14 @@ struct Sculpt::Keyboard_focus
|
||||
}
|
||||
|
||||
Keyboard_focus(Env &env,
|
||||
Network_dialog const &network_dialog,
|
||||
Network_widget const &network_widget,
|
||||
Wpa_passphrase &wpa_passphrase,
|
||||
Panel_dialog::State const &panel,
|
||||
System_dialog const &system_dialog,
|
||||
bool const &system_visible)
|
||||
:
|
||||
_focus_reporter(env, "focus", "focus"),
|
||||
_network_dialog(network_dialog),
|
||||
_network_widget(network_widget),
|
||||
_wpa_passphrase(wpa_passphrase),
|
||||
_panel(panel),
|
||||
_system_dialog(system_dialog),
|
||||
|
@ -38,13 +38,12 @@
|
||||
#include <model/settings.h>
|
||||
#include <model/presets.h>
|
||||
#include <model/screensaver.h>
|
||||
#include <view/download_status_dialog.h>
|
||||
#include <view/download_status_widget.h>
|
||||
#include <view/popup_dialog.h>
|
||||
#include <view/panel_dialog.h>
|
||||
#include <view/settings_dialog.h>
|
||||
#include <view/settings_widget.h>
|
||||
#include <view/system_dialog.h>
|
||||
#include <view/file_browser_dialog.h>
|
||||
#include <menu_view.h>
|
||||
#include <gui.h>
|
||||
#include <keyboard_focus.h>
|
||||
#include <network.h>
|
||||
@ -60,19 +59,18 @@ struct Sculpt::Main : Input_event_handler,
|
||||
Deploy::Action,
|
||||
Storage::Action,
|
||||
Network::Action,
|
||||
Network::Info,
|
||||
Graph::Action,
|
||||
Panel_dialog::Action,
|
||||
Popup_dialog::Action,
|
||||
Settings_dialog::Action,
|
||||
Software_presets_dialog::Action,
|
||||
Depot_users_dialog::Action,
|
||||
Software_update_dialog::Action,
|
||||
Settings_widget::Action,
|
||||
Software_presets_widget::Action,
|
||||
Software_update_widget::Action,
|
||||
File_browser_dialog::Action,
|
||||
Popup_dialog::Construction_info,
|
||||
Depot_query,
|
||||
Panel_dialog::State,
|
||||
Popup_dialog::Refresh,
|
||||
Menu_view::Hover_update_handler,
|
||||
Screensaver::Action
|
||||
{
|
||||
Env &_env;
|
||||
@ -150,7 +148,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_handle_gui_mode();
|
||||
|
||||
/* visibility of fonts section of settings dialog may have changed */
|
||||
_settings_menu_view.generate();
|
||||
_settings_dialog.refresh();
|
||||
|
||||
/* visibility of settings button may have changed */
|
||||
_refresh_panel_and_window_layout();
|
||||
@ -177,7 +175,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_event_filter_config.generate([&] (Xml_generator &xml) {
|
||||
_generate_event_filter_config(xml); });
|
||||
|
||||
_settings_menu_view.generate();
|
||||
_settings_dialog.refresh();
|
||||
|
||||
/* visibility of the settings dialog may have changed */
|
||||
if (orig_settings_available != _settings.interactive_settings_available()) {
|
||||
@ -186,6 +184,20 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
}
|
||||
|
||||
Dialog::Distant_runtime _dialog_runtime { _env };
|
||||
|
||||
template <typename TOP_LEVEL_DIALOG>
|
||||
struct Dialog_view : TOP_LEVEL_DIALOG, private Distant_runtime::View
|
||||
{
|
||||
template <typename... ARGS>
|
||||
Dialog_view(Distant_runtime &runtime, ARGS &&... args)
|
||||
: TOP_LEVEL_DIALOG(args...), Distant_runtime::View(runtime, *this) { }
|
||||
|
||||
using Distant_runtime::View::refresh;
|
||||
using Distant_runtime::View::min_width;
|
||||
using Distant_runtime::View::if_hovered;
|
||||
};
|
||||
|
||||
|
||||
/**********************
|
||||
** Device discovery **
|
||||
@ -229,6 +241,11 @@ struct Sculpt::Main : Input_event_handler,
|
||||
return _prepare_version.value != _prepare_completed.value;
|
||||
}
|
||||
|
||||
|
||||
/*************
|
||||
** Storage **
|
||||
*************/
|
||||
|
||||
Storage _storage { _env, _heap, _child_states, *this, *this };
|
||||
|
||||
/**
|
||||
@ -253,20 +270,53 @@ struct Sculpt::Main : Input_event_handler,
|
||||
*/
|
||||
void refresh_storage_dialog() override { _generate_dialog(); }
|
||||
|
||||
Network _network { _env, _heap, *this, _child_states, *this, _runtime_state, _pci_info };
|
||||
|
||||
Menu_view _network_menu_view { _env, _child_states, _network.dialog, "network_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"network_dialog", "network_view_hover",
|
||||
*this };
|
||||
/*************
|
||||
** Network **
|
||||
*************/
|
||||
|
||||
Network _network { _env, _heap, *this, *this, _child_states, *this, _runtime_state, _pci_info };
|
||||
|
||||
struct Network_top_level_dialog : Top_level_dialog
|
||||
{
|
||||
Main &_main;
|
||||
|
||||
Network_top_level_dialog(Main &main)
|
||||
: Top_level_dialog("network"), _main(main) { }
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
||||
_main._network.dialog.view(s); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at) override
|
||||
{
|
||||
_main._network.dialog.click(at, _main._network);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &) override { }
|
||||
void drag (Dragged_at const &) override { }
|
||||
};
|
||||
|
||||
Dialog_view<Network_top_level_dialog> _network_dialog { _dialog_runtime, *this };
|
||||
|
||||
/**
|
||||
* Network::Action interface
|
||||
*/
|
||||
void update_network_dialog() override
|
||||
{
|
||||
_network_menu_view.generate();
|
||||
_system_menu_view.generate();
|
||||
_network_dialog.refresh();
|
||||
_system_dialog.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Network::Info interface
|
||||
*/
|
||||
bool ap_list_hovered() const override
|
||||
{
|
||||
return _network_dialog.if_hovered([&] (Hovered_at const &at) {
|
||||
return _network.dialog.ap_list_hovered(at); });
|
||||
}
|
||||
|
||||
|
||||
@ -434,7 +484,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
void _handle_image_index()
|
||||
{
|
||||
_image_index_rom.update();
|
||||
_system_menu_view.generate();
|
||||
_system_dialog.refresh();
|
||||
}
|
||||
|
||||
Attached_rom_dataspace _launcher_listing_rom {
|
||||
@ -462,7 +512,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_presets.update_from_xml(dir); /* iterate over <file> nodes */
|
||||
});
|
||||
|
||||
_popup_menu_view.generate();
|
||||
_popup_dialog.refresh();
|
||||
_deploy._handle_managed_deploy();
|
||||
}
|
||||
|
||||
@ -516,14 +566,10 @@ struct Sculpt::Main : Input_event_handler,
|
||||
/**
|
||||
* Panel_dialog::State interface
|
||||
*/
|
||||
bool log_visible() const override { return _log_visible; }
|
||||
|
||||
bool network_visible() const override { return _network_visible; }
|
||||
|
||||
bool settings_visible() const override { return _settings_visible; }
|
||||
|
||||
bool system_visible() const override { return _system_visible; }
|
||||
|
||||
bool log_visible() const override { return _log_visible; }
|
||||
bool network_visible() const override { return _network_visible; }
|
||||
bool settings_visible() const override { return _settings_visible; }
|
||||
bool system_visible() const override { return _system_visible; }
|
||||
bool inspect_tab_visible() const override { return _storage.any_file_system_inspected(); }
|
||||
|
||||
Panel_dialog::Tab selected_tab() const override { return _selected_tab; }
|
||||
@ -575,7 +621,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
if (download_in_progress || _main._download_queue.any_failed_download()) {
|
||||
|
||||
Hosted<Vbox, Download_status_dialog> download_status { Id { "Download" } };
|
||||
Hosted<Vbox, Download_status_widget> download_status { Id { "Download" } };
|
||||
|
||||
s.widget(download_status, state, _main._download_queue);
|
||||
}
|
||||
@ -589,7 +635,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_graph_view.refresh();
|
||||
|
||||
if (_system_visible)
|
||||
_system_menu_view.generate();
|
||||
_system_dialog.refresh();
|
||||
}
|
||||
|
||||
Attached_rom_dataspace _runtime_state_rom { _env, "report -> runtime/state" };
|
||||
@ -651,7 +697,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_graph_view.refresh();
|
||||
|
||||
if (_selected_tab == Panel_dialog::Tab::FILES)
|
||||
_file_browser_menu_view.generate();
|
||||
_file_browser_dialog.refresh();
|
||||
}
|
||||
|
||||
|
||||
@ -659,83 +705,9 @@ struct Sculpt::Main : Input_event_handler,
|
||||
** Interactive operations **
|
||||
****************************/
|
||||
|
||||
Dialog::Distant_runtime _dialog_runtime { _env };
|
||||
|
||||
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> _clacked_seq_number { };
|
||||
|
||||
void _try_handle_click()
|
||||
{
|
||||
if (!_clicked_seq_number.constructed())
|
||||
return;
|
||||
|
||||
Input::Seq_number const seq = *_clicked_seq_number;
|
||||
|
||||
/* used to detect clicks outside the popup dialog (for closing it) */
|
||||
bool popup_dialog_clicked = false;
|
||||
bool const popup_opened = (_popup_opened_seq_number.value == seq.value);
|
||||
bool click_consumed = false;
|
||||
|
||||
if (_popup_menu_view.hovered(seq)) {
|
||||
_popup_dialog.click(*this);
|
||||
click_consumed = true;
|
||||
_popup_menu_view.generate();
|
||||
popup_dialog_clicked = true;
|
||||
}
|
||||
else if (_settings_menu_view.hovered(seq)) {
|
||||
_settings_dialog.click(*this);
|
||||
click_consumed = true;
|
||||
_settings_menu_view.generate();
|
||||
}
|
||||
else if (_system_menu_view.hovered(seq)) {
|
||||
_system_dialog.click();
|
||||
click_consumed = true;
|
||||
_system_menu_view.generate();
|
||||
}
|
||||
else if (_network_menu_view.hovered(seq)) {
|
||||
_network.dialog.click(_network);
|
||||
click_consumed = true;
|
||||
_network_menu_view.generate();
|
||||
}
|
||||
else if (_file_browser_menu_view.hovered(seq)) {
|
||||
_file_browser_dialog.click(*this);
|
||||
click_consumed = true;
|
||||
_file_browser_menu_view.generate();
|
||||
}
|
||||
|
||||
/* remove popup dialog when clicking somewhere outside */
|
||||
if (!popup_dialog_clicked && !_popup_menu_view._hovered && !popup_opened) {
|
||||
if (_popup.state == Popup::VISIBLE) {
|
||||
_close_popup_dialog();
|
||||
discard_construction();
|
||||
}
|
||||
}
|
||||
|
||||
if (click_consumed)
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
|
||||
void _try_handle_clack()
|
||||
{
|
||||
if (!_clacked_seq_number.constructed())
|
||||
return;
|
||||
|
||||
Input::Seq_number const seq = *_clacked_seq_number;
|
||||
|
||||
if (_system_menu_view.hovered(seq)) {
|
||||
_system_dialog.clack();
|
||||
_system_menu_view.generate();
|
||||
_clacked_seq_number.destruct();
|
||||
}
|
||||
else if (_popup_menu_view.hovered(seq)) {
|
||||
_popup_dialog.clack(*this);
|
||||
_clacked_seq_number.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
struct Keyboard_focus_guard
|
||||
{
|
||||
Main &_main;
|
||||
@ -745,19 +717,9 @@ struct Sculpt::Main : Input_event_handler,
|
||||
~Keyboard_focus_guard() { _main._keyboard_focus.update(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Menu_view::Hover_update_handler interface
|
||||
*/
|
||||
void menu_view_hover_updated() override
|
||||
{
|
||||
Keyboard_focus_guard focus_guard { *this };
|
||||
|
||||
if (_clicked_seq_number.constructed())
|
||||
_try_handle_click();
|
||||
|
||||
if (_clacked_seq_number.constructed())
|
||||
_try_handle_clack();
|
||||
}
|
||||
/* used to prevent closing the popup immediatedly after opened */
|
||||
Input::Seq_number _popup_opened_seq_number { };
|
||||
Input::Seq_number _clicked_seq_number { };
|
||||
|
||||
/**
|
||||
* Input_event_handler interface
|
||||
@ -770,14 +732,25 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
_dialog_runtime.route_input_event(seq_number, ev);
|
||||
|
||||
/*
|
||||
* Detect clicks outside the popup dialog (for closing it)
|
||||
*/
|
||||
if (ev.key_press(Input::BTN_LEFT) || ev.touch()) {
|
||||
_clicked_seq_number.construct(_global_input_seq_number);
|
||||
_try_handle_click();
|
||||
}
|
||||
|
||||
if (ev.key_release(Input::BTN_LEFT)) {
|
||||
_clacked_seq_number.construct(_global_input_seq_number);
|
||||
_try_handle_clack();
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool need_generate_dialog = false;
|
||||
@ -786,7 +759,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|
||||
_network.handle_key_press(code);
|
||||
else if (_system_visible && _system_dialog.keyboard_needed())
|
||||
_system_dialog.handle_key(code);
|
||||
_system_dialog.handle_key(code, *this);
|
||||
|
||||
need_generate_dialog = true;
|
||||
});
|
||||
@ -808,13 +781,13 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
void use(Storage_target const &target) override
|
||||
{
|
||||
_system_dialog.reset_update_dialog();
|
||||
_system_dialog.reset_update_widget();
|
||||
_download_queue.reset();
|
||||
_storage.use(target);
|
||||
|
||||
/* hide system panel button and system dialog when "un-using" */
|
||||
_panel_dialog.refresh();
|
||||
_system_menu_view.generate();
|
||||
_system_dialog.refresh();
|
||||
_handle_window_layout();
|
||||
}
|
||||
|
||||
@ -824,7 +797,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
/*
|
||||
* Storage_dialog::Action interface
|
||||
* Storage_widget::Action interface
|
||||
*/
|
||||
void format(Storage_target const &target) override
|
||||
{
|
||||
@ -898,9 +871,6 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
}
|
||||
|
||||
/* used to prevent closing the popup immediatedly after opened */
|
||||
Input::Seq_number _popup_opened_seq_number { };
|
||||
|
||||
/*
|
||||
* Graph::Action interface
|
||||
*/
|
||||
@ -909,10 +879,9 @@ struct Sculpt::Main : Input_event_handler,
|
||||
if (_popup.state == Popup::VISIBLE)
|
||||
return;
|
||||
|
||||
if (_clicked_seq_number.constructed())
|
||||
_popup_opened_seq_number = *_clicked_seq_number;
|
||||
_popup_opened_seq_number = _clicked_seq_number;
|
||||
|
||||
_popup_menu_view.generate();
|
||||
_popup_dialog.refresh();
|
||||
_popup.anchor = anchor;
|
||||
_popup.state = Popup::VISIBLE;
|
||||
_graph_view.refresh();
|
||||
@ -942,7 +911,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
/**
|
||||
* Software_update_dialog::Action interface
|
||||
* Software_update_widget::Action interface
|
||||
*/
|
||||
void query_image_index(Depot::Archive::User const &user) override
|
||||
{
|
||||
@ -951,7 +920,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
/**
|
||||
* Software_update_dialog::Action interface
|
||||
* Software_update_widget::Action interface
|
||||
*/
|
||||
void trigger_image_download(Path const &path, Verify verify) override
|
||||
{
|
||||
@ -962,7 +931,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
/**
|
||||
* Software_update_dialog::Action interface
|
||||
* Software_update_widget::Action interface
|
||||
*/
|
||||
void update_image_index(Depot::Archive::User const &user, Verify verify) override
|
||||
{
|
||||
@ -973,7 +942,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
/**
|
||||
* Software_update_dialog::Action interface
|
||||
* Software_update_widget::Action interface
|
||||
*/
|
||||
void install_boot_image(Path const &path) override
|
||||
{
|
||||
@ -993,7 +962,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_selected_tab = tab;
|
||||
|
||||
if (_selected_tab == Panel_dialog::Tab::FILES)
|
||||
_file_browser_menu_view.generate();
|
||||
_file_browser_dialog.refresh();
|
||||
|
||||
_refresh_panel_and_window_layout();
|
||||
}
|
||||
@ -1050,14 +1019,30 @@ struct Sculpt::Main : Input_event_handler,
|
||||
file.with_optional_sub_node("config", [&] (Xml_node const &config) {
|
||||
_runtime_state.reset_abandoned_and_launched_children();
|
||||
_deploy.use_as_deploy_template(config);
|
||||
_deploy.update_managed_deploy_config();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
_deploy.update_managed_deploy_config(); }); } }); } });
|
||||
}
|
||||
|
||||
struct Settings_top_level_dialog : Top_level_dialog
|
||||
{
|
||||
Main &_main;
|
||||
|
||||
Hosted<Frame, Settings_widget> _hosted { Id { "hosted" }, _main._settings };
|
||||
|
||||
Settings_top_level_dialog(Main &main)
|
||||
: Top_level_dialog("settings"), _main(main) { }
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) { s.widget(_hosted); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at) override { _hosted.propagate(at, _main); }
|
||||
void clack(Clacked_at const &) override { }
|
||||
void drag (Dragged_at const &) override { }
|
||||
};
|
||||
|
||||
Dialog_view<Settings_top_level_dialog> _settings_dialog { _dialog_runtime, *this };
|
||||
|
||||
/*
|
||||
* Settings_dialog::Action interface
|
||||
*/
|
||||
@ -1089,7 +1074,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
void _handle_fs_query_result()
|
||||
{
|
||||
_file_browser_state.update_query_results();
|
||||
_file_browser_menu_view.generate();
|
||||
_file_browser_dialog.refresh();
|
||||
}
|
||||
|
||||
Signal_handler<Main> _editor_saved_handler {
|
||||
@ -1107,7 +1092,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_file_browser_state.last_saved_version = saved.attribute_value("version", 0U);
|
||||
|
||||
if (orig_modified != _file_browser_state.modified)
|
||||
_file_browser_menu_view.generate();
|
||||
_file_browser_dialog.refresh();
|
||||
}
|
||||
|
||||
void _close_edited_file()
|
||||
@ -1139,7 +1124,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
Priority::LEITZENTRALE,
|
||||
Ram_quota{8*1024*1024}, Cap_quota{200});
|
||||
|
||||
Label const rom_label("report -> /runtime/", start_name, "/listing");
|
||||
Service::Label const rom_label("report -> /runtime/", start_name, "/listing");
|
||||
|
||||
_file_browser_state.query_result.construct(_env, rom_label.string());
|
||||
_file_browser_state.query_result->sigh(_fs_query_result_handler);
|
||||
@ -1148,7 +1133,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
generate_runtime_config();
|
||||
|
||||
_file_browser_menu_view.generate();
|
||||
_file_browser_dialog.refresh();
|
||||
}
|
||||
|
||||
void browse_sub_directory(File_browser_state::Sub_dir const &sub_dir) override
|
||||
@ -1236,7 +1221,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
/* close popup menu */
|
||||
_popup.state = Popup::OFF;
|
||||
_popup_dialog.reset();
|
||||
_popup_menu_view.generate();
|
||||
_popup_dialog.refresh();
|
||||
|
||||
/* remove popup window from window layout */
|
||||
_handle_window_layout();
|
||||
@ -1317,55 +1302,30 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_runtime_state.with_construction([&] (Component const &c) { fn.with(c); });
|
||||
}
|
||||
|
||||
template <typename TOP_LEVEL_DIALOG>
|
||||
struct Dialog_view : TOP_LEVEL_DIALOG, private Distant_runtime::View
|
||||
{
|
||||
template <typename... ARGS>
|
||||
Dialog_view(Distant_runtime &runtime, ARGS &&... args)
|
||||
: TOP_LEVEL_DIALOG(args...), Distant_runtime::View(runtime, *this) { }
|
||||
|
||||
using Distant_runtime::View::refresh;
|
||||
using Distant_runtime::View::min_width;
|
||||
};
|
||||
|
||||
Dialog_view<Panel_dialog> _panel_dialog { _dialog_runtime, *this, *this };
|
||||
|
||||
Settings_dialog _settings_dialog { _settings };
|
||||
|
||||
Menu_view _settings_menu_view { _env, _child_states, _settings_dialog, "settings_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"settings_dialog", "settings_view_hover", *this };
|
||||
|
||||
System_dialog _system_dialog { _presets, _build_info, _network._nic_state,
|
||||
_download_queue, _index_update_queue,
|
||||
_file_operation_queue, _scan_rom,
|
||||
_image_index_rom, *this, *this, *this };
|
||||
|
||||
Menu_view _system_menu_view { _env, _child_states, _system_dialog, "system_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"system_dialog", "system_view_hover", *this };
|
||||
Dialog_view<System_dialog> _system_dialog { _dialog_runtime,
|
||||
_presets, _build_info, _network._nic_state,
|
||||
_download_queue, _index_update_queue,
|
||||
_file_operation_queue, _scan_rom,
|
||||
_image_index_rom, *this, *this };
|
||||
|
||||
Dialog_view<Diag_dialog> _diag_dialog { _dialog_runtime, *this, _heap };
|
||||
|
||||
Popup_dialog _popup_dialog { _env, *this, _launchers,
|
||||
_network._nic_state, _network._nic_target,
|
||||
_runtime_state, _cached_runtime_config,
|
||||
_download_queue, _scan_rom, *this, *this };
|
||||
Dialog_view<Popup_dialog> _popup_dialog { _dialog_runtime, _env, *this, *this,
|
||||
_launchers, _network._nic_state,
|
||||
_network._nic_target, _runtime_state,
|
||||
_cached_runtime_config, _download_queue,
|
||||
_scan_rom, *this, *this };
|
||||
|
||||
Menu_view _popup_menu_view { _env, _child_states, _popup_dialog, "popup_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"popup_dialog", "popup_view_hover", *this };
|
||||
|
||||
File_browser_dialog _file_browser_dialog { _cached_runtime_config, _file_browser_state };
|
||||
|
||||
Menu_view _file_browser_menu_view { _env, _child_states, _file_browser_dialog, "file_browser_view",
|
||||
Ram_quota{8*1024*1024}, Cap_quota{150},
|
||||
"file_browser_dialog", "file_browser_view_hover", *this };
|
||||
Dialog_view<File_browser_dialog> _file_browser_dialog { _dialog_runtime,
|
||||
_cached_runtime_config,
|
||||
_file_browser_state, *this };
|
||||
|
||||
/**
|
||||
* Popup_dialog::Refresh interface
|
||||
*/
|
||||
void refresh_popup_dialog() override { _popup_menu_view.generate(); }
|
||||
void refresh_popup_dialog() override { _popup_dialog.refresh(); }
|
||||
|
||||
Managed_config<Main> _fb_drv_config {
|
||||
_env, "config", "fb_drv", *this, &Main::_handle_fb_drv_config };
|
||||
@ -1412,27 +1372,21 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
struct Graph_dialog : Dialog::Top_level_dialog
|
||||
{
|
||||
Graph &_graph;
|
||||
Graph::Action &_action;
|
||||
Ram_fs_dialog::Action &_ram_fs_action;
|
||||
Main &_main;
|
||||
|
||||
Graph_dialog(Graph &graph, Graph::Action &action, Ram_fs_dialog::Action &ram_fs_action)
|
||||
:
|
||||
Top_level_dialog("runtime"),
|
||||
_graph(graph), _action(action), _ram_fs_action(ram_fs_action)
|
||||
{ }
|
||||
Graph_dialog(Main &main) : Top_level_dialog("runtime"), _main(main) { }
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
s.sub_scope<Depgraph>([&] (Scope<Depgraph> &s) {
|
||||
_graph.view(s); });
|
||||
_main._graph.view(s); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at) override { _graph.click(at, _action); }
|
||||
void clack(Clacked_at const &at) override { _graph.clack(at, _action, _ram_fs_action); }
|
||||
void click(Clicked_at const &at) override { _main._graph.click(at, _main); }
|
||||
void clack(Clacked_at const &at) override { _main._graph.clack(at, _main, _main._storage); }
|
||||
void drag (Dragged_at const &) override { }
|
||||
|
||||
} _graph_dialog { _graph, *this, _storage };
|
||||
} _graph_dialog { *this };
|
||||
|
||||
Dialog::Distant_runtime::View
|
||||
_graph_view { _dialog_runtime, _graph_dialog,
|
||||
@ -1808,7 +1762,7 @@ void Sculpt::Main::_handle_gui_mode()
|
||||
_panel_dialog.min_width = _screen_size.w();
|
||||
unsigned const menu_width = max((unsigned)(_font_size_px*21.0), 320u);
|
||||
_diag_dialog.min_width = menu_width;
|
||||
_network_menu_view.min_width = menu_width;
|
||||
_network_dialog.min_width = menu_width;
|
||||
|
||||
/* font size may has changed, propagate fonts config of runtime view */
|
||||
generate_runtime_config();
|
||||
@ -2098,11 +2052,6 @@ void Sculpt::Main::_generate_runtime_config(Xml_generator &xml) const
|
||||
});
|
||||
|
||||
_dialog_runtime.gen_start_nodes(xml);
|
||||
_settings_menu_view.gen_start_node(xml);
|
||||
_system_menu_view.gen_start_node(xml);
|
||||
_network_menu_view.gen_start_node(xml);
|
||||
_popup_menu_view.gen_start_node(xml);
|
||||
_file_browser_menu_view.gen_start_node(xml);
|
||||
|
||||
_storage.gen_runtime_start_nodes(xml);
|
||||
_file_browser_state.gen_start_nodes(xml);
|
||||
|
@ -29,8 +29,9 @@ struct Sculpt::Managed_config
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
typedef String<20> Xml_node_name;
|
||||
typedef String<32> Rom_name;
|
||||
using Xml_node_name = String<20>;
|
||||
using Rom_name = String<32>;
|
||||
using Label = Session_label;
|
||||
|
||||
enum Mode { MANAGED, MANUAL } _mode { MANAGED };
|
||||
|
||||
|
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* \brief Menu-view dialog handling
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <menu_view.h>
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
|
||||
void Menu_view::_handle_hover()
|
||||
{
|
||||
using Hover_result = Deprecated_dialog::Hover_result;
|
||||
|
||||
_hover_rom.update();
|
||||
|
||||
bool const orig_hovered = _hovered;
|
||||
|
||||
_hovered = false;
|
||||
|
||||
Hover_result hover_result = Hover_result::UNMODIFIED;
|
||||
|
||||
Xml_node hover = _hover_rom.xml();
|
||||
|
||||
if (hover.has_attribute("seq_number")) {
|
||||
Input::Seq_number const seq { hover.attribute_value("seq_number", 0U) };
|
||||
_seq_number.construct(seq);
|
||||
}
|
||||
|
||||
hover.with_optional_sub_node("dialog", [&] (Xml_node hover) {
|
||||
_hovered = true;
|
||||
hover_result = _dialog.hover(hover);
|
||||
});
|
||||
|
||||
if (!_hovered)
|
||||
_dialog.hover(Xml_node("<empty/>"));
|
||||
|
||||
bool const dialog_hover_changed = (_hovered != orig_hovered),
|
||||
widget_hover_changed = (hover_result == Hover_result::CHANGED);
|
||||
|
||||
if (dialog_hover_changed || widget_hover_changed)
|
||||
generate();
|
||||
|
||||
/* trigger handling of pending click/clack actions */
|
||||
_hover_update_handler.menu_view_hover_updated();
|
||||
}
|
||||
|
||||
|
||||
Menu_view::Menu_view(Env &env, Registry<Child_state> ®istry,
|
||||
Deprecated_dialog &dialog, Start_name const &name,
|
||||
Ram_quota ram_quota, Cap_quota cap_quota,
|
||||
Session_label const &dialog_report_name,
|
||||
Session_label const &hover_rom_name,
|
||||
Hover_update_handler &hover_update_handler,
|
||||
Alpha alpha, Color background_color)
|
||||
:
|
||||
_dialog(dialog),
|
||||
_hover_update_handler(hover_update_handler),
|
||||
_child_state(registry, name, Priority::LEITZENTRALE, ram_quota, cap_quota),
|
||||
_dialog_reporter(env, "dialog", dialog_report_name.string()),
|
||||
_hover_rom(env, hover_rom_name.string()),
|
||||
_hover_handler(env.ep(), *this, &Menu_view::_handle_hover),
|
||||
_opaque(alpha == Alpha::OPAQUE), _background_color(background_color)
|
||||
{
|
||||
_hover_rom.sigh(_hover_handler);
|
||||
|
||||
generate();
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::generate()
|
||||
{
|
||||
_dialog_reporter.generate([&] (Xml_generator &xml) {
|
||||
_dialog.generate(xml); });
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::reset()
|
||||
{
|
||||
_hovered = false;
|
||||
_dialog.hover(Xml_node("<empty/>"));
|
||||
_dialog.reset();
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::gen_start_node(Xml_generator &xml) const
|
||||
{
|
||||
xml.node("start", [&] () {
|
||||
_gen_start_node_content(xml); });
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::_gen_start_node_content(Xml_generator &xml) const
|
||||
{
|
||||
_child_state.gen_start_node_content(xml);
|
||||
|
||||
gen_named_node(xml, "resource", "CPU", [&] () {
|
||||
xml.attribute("quantum", 10); });
|
||||
|
||||
gen_named_node(xml, "binary", "menu_view");
|
||||
|
||||
xml.node("config", [&] () {
|
||||
if (min_width) xml.attribute("width", min_width);
|
||||
if (min_height) xml.attribute("height", min_height);
|
||||
if (_opaque) xml.attribute("opaque", "yes");
|
||||
|
||||
xml.attribute("background", String<20>(_background_color));
|
||||
|
||||
xml.node("libc", [&] () { xml.attribute("stderr", "/dev/log"); });
|
||||
xml.node("report", [&] () { xml.attribute("hover", "yes"); });
|
||||
xml.node("vfs", [&] () {
|
||||
gen_named_node(xml, "tar", "menu_view_styles.tar");
|
||||
|
||||
gen_named_node(xml, "dir", "fonts", [&] () {
|
||||
xml.node("fs", [&] () {
|
||||
xml.attribute("label", "fonts"); }); });
|
||||
|
||||
gen_named_node(xml, "dir", "dev", [&] () {
|
||||
xml.node("log", [&] () { }); });
|
||||
});
|
||||
});
|
||||
|
||||
xml.node("route", [&] () {
|
||||
gen_parent_rom_route(xml, "menu_view");
|
||||
gen_parent_rom_route(xml, "ld.lib.so");
|
||||
gen_parent_rom_route(xml, "vfs.lib.so");
|
||||
gen_parent_rom_route(xml, "libc.lib.so");
|
||||
gen_parent_rom_route(xml, "libm.lib.so");
|
||||
gen_parent_rom_route(xml, "libpng.lib.so");
|
||||
gen_parent_rom_route(xml, "zlib.lib.so");
|
||||
gen_parent_rom_route(xml, "menu_view_styles.tar");
|
||||
gen_parent_route<Cpu_session> (xml);
|
||||
gen_parent_route<Pd_session> (xml);
|
||||
gen_parent_route<Log_session> (xml);
|
||||
gen_parent_route<Timer::Session> (xml);
|
||||
|
||||
using Label = String<128>;
|
||||
|
||||
Label const label = _child_state.name();
|
||||
|
||||
gen_service_node<Gui::Session>(xml, [&] () {
|
||||
xml.node("parent", [&] () {
|
||||
xml.attribute("label", Label("leitzentrale -> ", label)); }); });
|
||||
|
||||
gen_service_node<Rom_session>(xml, [&] () {
|
||||
xml.attribute("label", "dialog");
|
||||
xml.node("parent", [&] () {
|
||||
xml.attribute("label", Label("leitzentrale -> ", label, " -> dialog"));
|
||||
});
|
||||
});
|
||||
|
||||
gen_service_node<Report::Session>(xml, [&] () {
|
||||
xml.attribute("label", "hover");
|
||||
xml.node("parent", [&] () {
|
||||
xml.attribute("label", Label("leitzentrale -> ", label, " -> hover"));
|
||||
});
|
||||
});
|
||||
|
||||
gen_service_node<::File_system::Session>(xml, [&] () {
|
||||
xml.attribute("label", "fonts");
|
||||
xml.node("parent", [&] () {
|
||||
xml.attribute("label", "leitzentrale -> fonts"); }); });
|
||||
});
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* \brief Menu-view dialog handling
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _MENU_VIEW_H_
|
||||
#define _MENU_VIEW_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/session_label.h>
|
||||
#include <os/reporter.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
#include <view/dialog.h>
|
||||
#include <model/child_state.h>
|
||||
|
||||
namespace Sculpt { struct Menu_view; }
|
||||
|
||||
|
||||
struct Sculpt::Menu_view : Noncopyable
|
||||
{
|
||||
struct Hover_update_handler : Interface, Noncopyable
|
||||
{
|
||||
virtual void menu_view_hover_updated() = 0;
|
||||
};
|
||||
|
||||
Deprecated_dialog &_dialog;
|
||||
|
||||
Hover_update_handler &_hover_update_handler;
|
||||
|
||||
Child_state _child_state;
|
||||
|
||||
Expanding_reporter _dialog_reporter;
|
||||
|
||||
Attached_rom_dataspace _hover_rom;
|
||||
|
||||
Signal_handler<Menu_view> _hover_handler;
|
||||
|
||||
bool const _opaque;
|
||||
|
||||
Color const _background_color;
|
||||
|
||||
bool _hovered = false;
|
||||
|
||||
Constructible<Input::Seq_number> _seq_number { };
|
||||
|
||||
unsigned min_width = 0;
|
||||
unsigned min_height = 0;
|
||||
|
||||
void _handle_hover();
|
||||
|
||||
void _gen_start_node_content(Xml_generator &) const;
|
||||
|
||||
enum class Alpha { OPAQUE, ALPHA };
|
||||
|
||||
Menu_view(Env &, Registry<Child_state> ®istry,
|
||||
Deprecated_dialog &, Start_name const &, Ram_quota, Cap_quota,
|
||||
Session_label const &dialog_report_name,
|
||||
Session_label const &hover_rom_name,
|
||||
Hover_update_handler &,
|
||||
Alpha alpha = Alpha::ALPHA,
|
||||
Color background = Color { 127, 127, 127, 255 });
|
||||
|
||||
void generate();
|
||||
|
||||
bool hovered(Input::Seq_number const &seq_number) const
|
||||
{
|
||||
if (!_seq_number.constructed() || !_hovered)
|
||||
return false;
|
||||
|
||||
return (_seq_number->value >= seq_number.value);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
void gen_start_node(Xml_generator &) const;
|
||||
|
||||
bool apply_child_state_report(Xml_node report)
|
||||
{
|
||||
return _child_state.apply_child_state_report(report);
|
||||
}
|
||||
|
||||
void trigger_restart() { _child_state.trigger_restart(); }
|
||||
};
|
||||
|
||||
#endif /* _MENU_VIEW_H_ */
|
@ -20,6 +20,7 @@
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
#include <xml.h>
|
||||
|
||||
namespace Sculpt { struct Child_state; }
|
||||
|
||||
|
@ -61,16 +61,14 @@ struct Sculpt::File_browser_state : Noncopyable
|
||||
fn(query_result->xml());
|
||||
}
|
||||
|
||||
using Index = Label;
|
||||
|
||||
template <typename FN>
|
||||
void with_entry_at_index(Index index, FN const &fn) const
|
||||
void with_entry_at_index(unsigned const index, FN const &fn) const
|
||||
{
|
||||
unsigned cnt = 0;
|
||||
unsigned count = 0;
|
||||
with_query_result([&] (Xml_node node) {
|
||||
node.with_optional_sub_node("dir", [&] (Xml_node listing) {
|
||||
listing.for_each_sub_node([&] (Xml_node entry) {
|
||||
if (Index(cnt++) == index)
|
||||
if (count++ == index)
|
||||
fn(entry); }); }); });
|
||||
}
|
||||
|
||||
@ -187,7 +185,7 @@ struct Sculpt::File_browser_state : Noncopyable
|
||||
|
||||
gen_service_node<Gui::Session>(xml, [&] () {
|
||||
xml.node("parent", [&] () {
|
||||
xml.attribute("label", Label("leitzentrale -> editor")); }); });
|
||||
xml.attribute("label", "leitzentrale -> editor"); }); });
|
||||
|
||||
gen_service_node<::File_system::Session>(xml, [&] () {
|
||||
xml.attribute("label", "fonts");
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _MODEL__ROUTE_H_
|
||||
#define _MODEL__ROUTE_H_
|
||||
|
||||
#include <xml.h>
|
||||
#include <types.h>
|
||||
#include <model/service.h>
|
||||
|
||||
@ -22,8 +23,9 @@ namespace Sculpt { struct Route; }
|
||||
|
||||
struct Sculpt::Route : List_model<Route>::Element
|
||||
{
|
||||
typedef String<32> Id;
|
||||
typedef String<80> Info;
|
||||
using Id = String<32>;
|
||||
using Info = String<80>;
|
||||
using Label = Service::Label;
|
||||
|
||||
static char const *xml_type(Service::Type type)
|
||||
{
|
||||
|
@ -60,8 +60,8 @@ class Sculpt::Runtime_config
|
||||
Service::Type_name const service =
|
||||
node.attribute_value("name", Service::Type_name());
|
||||
|
||||
Label const dst_label =
|
||||
parent.attribute_value("label", Label());
|
||||
Service::Label const dst_label =
|
||||
parent.attribute_value("label", Service::Label());
|
||||
|
||||
bool const ignored_service = (service == "CPU")
|
||||
|| (service == "PD")
|
||||
@ -167,7 +167,7 @@ class Sculpt::Runtime_config
|
||||
}
|
||||
|
||||
Child_service(Start_name server, Xml_node provides)
|
||||
: Service(server, type_from_xml(provides), Label()) { }
|
||||
: Service(server, type_from_xml(provides), { }) { }
|
||||
|
||||
static bool type_matches(Xml_node const &node)
|
||||
{
|
||||
@ -356,7 +356,7 @@ class Sculpt::Runtime_config
|
||||
|
||||
Service const _used_fs_service { "default_fs_rw",
|
||||
Service::Type::FILE_SYSTEM,
|
||||
Label(), "used file system" };
|
||||
{ }, "used file system" };
|
||||
|
||||
public:
|
||||
|
||||
@ -428,6 +428,15 @@ class Sculpt::Runtime_config
|
||||
_components.for_each([&] (Component const &component) {
|
||||
component.for_each_service(fn); });
|
||||
}
|
||||
|
||||
unsigned num_service_options(Service::Type const type) const
|
||||
{
|
||||
unsigned count = 0;
|
||||
for_each_service([&] (Service const &service) {
|
||||
if (service.type == type)
|
||||
count++; });
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _MODEL__RUNTIME_CONFIG_H_ */
|
||||
|
@ -21,8 +21,9 @@ namespace Sculpt { struct Service; }
|
||||
|
||||
struct Sculpt::Service
|
||||
{
|
||||
typedef String<16> Type_name;
|
||||
typedef String<32> Info;
|
||||
using Type_name = String<16>;
|
||||
using Info = String<32>;
|
||||
using Label = String<64>;
|
||||
|
||||
enum class Type {
|
||||
AUDIO_IN, AUDIO_OUT, BLOCK, EVENT, CAPTURE, FILE_SYSTEM, NIC, GUI, GPU,
|
||||
|
@ -40,7 +40,7 @@ void Sculpt::Network::handle_key_press(Codepoint code)
|
||||
wpa_passphrase.remove_last_character();
|
||||
else if (code.value == ENTER) {
|
||||
if (wpa_passphrase.suitable_for_connect())
|
||||
wifi_connect(dialog.selected_ap());
|
||||
wifi_connect(dialog._ap_selector._selected);
|
||||
}
|
||||
else if (code.valid())
|
||||
wpa_passphrase.append_character(code);
|
||||
@ -124,7 +124,7 @@ void Sculpt::Network::_handle_wlan_accesspoints()
|
||||
_wlan_accesspoints_rom.update();
|
||||
|
||||
/* suppress updating the list while the access-point list is hovered */
|
||||
if (!initial_scan && dialog.ap_list_hovered())
|
||||
if (!initial_scan && _info.ap_list_hovered())
|
||||
return;
|
||||
|
||||
_access_points.update_from_xml(_wlan_accesspoints_rom.xml(),
|
||||
|
@ -21,8 +21,7 @@
|
||||
/* local includes */
|
||||
#include <model/child_exit_state.h>
|
||||
#include <model/pci_info.h>
|
||||
#include <view/network_dialog.h>
|
||||
#include <menu_view.h>
|
||||
#include <view/network_widget.h>
|
||||
#include <runtime.h>
|
||||
#include <keyboard_focus.h>
|
||||
#include <managed_config.h>
|
||||
@ -30,7 +29,7 @@
|
||||
namespace Sculpt { struct Network; }
|
||||
|
||||
|
||||
struct Sculpt::Network : Network_dialog::Action
|
||||
struct Sculpt::Network : Network_widget::Action
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
@ -41,7 +40,13 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
virtual void update_network_dialog() = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
struct Info : Interface
|
||||
{
|
||||
virtual bool ap_list_hovered() const = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
Info const &_info;
|
||||
|
||||
Registry<Child_state> &_child_states;
|
||||
|
||||
@ -50,9 +55,13 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
Runtime_info const &_runtime_info;
|
||||
Pci_info const &_pci_info;
|
||||
|
||||
using Wlan_config_policy = Network_widget::Wlan_config_policy;
|
||||
|
||||
Nic_target _nic_target { };
|
||||
Nic_state _nic_state { };
|
||||
|
||||
Access_point::Bssid _selected_ap { };
|
||||
|
||||
Wpa_passphrase wpa_passphrase { };
|
||||
|
||||
unsigned _nic_drv_version = 0;
|
||||
@ -100,10 +109,9 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
Signal_handler<Network> _nic_router_state_handler {
|
||||
_env.ep(), *this, &Network::_handle_nic_router_state };
|
||||
|
||||
Network_dialog::Wlan_config_policy _wlan_config_policy =
|
||||
Network_dialog::WLAN_CONFIG_MANAGED;
|
||||
Wlan_config_policy _wlan_config_policy = Wlan_config_policy::MANAGED;
|
||||
|
||||
Network_dialog dialog {
|
||||
Network_widget dialog {
|
||||
_nic_target, _access_points,
|
||||
_wifi_connection, _nic_state, wpa_passphrase, _wlan_config_policy,
|
||||
_pci_info };
|
||||
@ -114,12 +122,12 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
void _handle_wlan_config(Xml_node)
|
||||
{
|
||||
if (_wlan_config.try_generate_manually_managed()) {
|
||||
_wlan_config_policy = Network_dialog::WLAN_CONFIG_MANUAL;
|
||||
_wlan_config_policy = Wlan_config_policy::MANUAL;
|
||||
_action.update_network_dialog();
|
||||
return;
|
||||
}
|
||||
|
||||
_wlan_config_policy = Network_dialog::WLAN_CONFIG_MANAGED;;
|
||||
_wlan_config_policy = Wlan_config_policy::MANAGED;;
|
||||
|
||||
if (_wifi_connection.connected())
|
||||
wifi_connect(_wifi_connection.bssid);
|
||||
@ -130,7 +138,7 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
void _update_nic_target_from_config(Xml_node const &);
|
||||
|
||||
/**
|
||||
* Network_dialog::Action interface
|
||||
* Network_widget::Action interface
|
||||
*/
|
||||
void nic_target(Nic_target::Type const type) override
|
||||
{
|
||||
@ -207,12 +215,13 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
_runtime_config_generator.generate_runtime_config();
|
||||
}
|
||||
|
||||
Network(Env &env, Allocator &alloc, Action &action,
|
||||
Network(Env &env, Allocator &alloc, Action &action, Info const &info,
|
||||
Registry<Child_state> &child_states,
|
||||
Runtime_config_generator &runtime_config_generator,
|
||||
Runtime_info const &runtime_info, Pci_info const &pci_info)
|
||||
:
|
||||
_env(env), _alloc(alloc), _action(action), _child_states(child_states),
|
||||
_env(env), _alloc(alloc), _action(action), _info(info),
|
||||
_child_states(child_states),
|
||||
_runtime_config_generator(runtime_config_generator),
|
||||
_runtime_info(runtime_info), _pci_info(pci_info)
|
||||
{
|
||||
|
@ -20,14 +20,14 @@
|
||||
|
||||
/* local includes */
|
||||
#include <model/discovery_state.h>
|
||||
#include <view/storage_dialog.h>
|
||||
#include <view/ram_fs_dialog.h>
|
||||
#include <view/storage_widget.h>
|
||||
#include <view/ram_fs_widget.h>
|
||||
#include <runtime.h>
|
||||
|
||||
namespace Sculpt { struct Storage; }
|
||||
|
||||
|
||||
struct Sculpt::Storage : Storage_device_dialog::Action, Ram_fs_dialog::Action
|
||||
struct Sculpt::Storage : Storage_device_widget::Action, Ram_fs_widget::Action
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
@ -101,7 +101,7 @@ struct Sculpt::Storage : Storage_device_dialog::Action, Ram_fs_dialog::Action
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage_dialog::Action interface
|
||||
* Storage_widget::Action interface
|
||||
*/
|
||||
void format(Storage_target const &target) override
|
||||
{
|
||||
|
@ -40,7 +40,6 @@ namespace Sculpt {
|
||||
typedef String<64> Rom_name;
|
||||
typedef String<128> Path;
|
||||
typedef String<36> Start_name;
|
||||
typedef String<64> Label;
|
||||
|
||||
typedef Gui::Point Point;
|
||||
typedef Gui::Rect Rect;
|
||||
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* \brief GUI element that can be activated on clack
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__ACTIVATABLE_ITEM_H_
|
||||
#define _VIEW__ACTIVATABLE_ITEM_H_
|
||||
|
||||
#include "hoverable_item.h"
|
||||
|
||||
namespace Sculpt { struct Activatable_item; }
|
||||
|
||||
|
||||
struct Sculpt::Activatable_item : Hoverable_item
|
||||
{
|
||||
typedef Hoverable_item::Id Id;
|
||||
|
||||
Id _selected { };
|
||||
Id _activated { };
|
||||
|
||||
/**
|
||||
* Apply click - if item is hovered, the click selects the item but
|
||||
* does not activate it yet
|
||||
*/
|
||||
void propose_activation_on_click()
|
||||
{
|
||||
_selected = _hovered;
|
||||
}
|
||||
|
||||
void confirm_activation_on_clack()
|
||||
{
|
||||
if (_hovered.valid() && (_hovered == _selected))
|
||||
_activated = _selected;
|
||||
}
|
||||
|
||||
void reset() { _selected = Id{}, _activated = Id{}; }
|
||||
|
||||
/**
|
||||
* Return true if item is currently activated
|
||||
*/
|
||||
bool activated(Id const &id) const { return id == _activated; }
|
||||
|
||||
/**
|
||||
* Generate button attributes depending on the item state
|
||||
*/
|
||||
void gen_button_attr(Xml_generator &xml, Id const &id) const
|
||||
{
|
||||
/* hover only as long as the button is not activated */
|
||||
if (!_selected.valid() || !_activated.valid())
|
||||
Hoverable_item::gen_button_attr(xml, id);
|
||||
|
||||
if (_selected.valid() && _selected == _hovered && _selected == id)
|
||||
xml.attribute("selected", "yes");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__ACTIVATABLE_ITEM_H_ */
|
231
repos/gems/src/app/sculpt_manager/view/ap_selector_widget.h
Normal file
231
repos/gems/src/app/sculpt_manager/view/ap_selector_widget.h
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* \brief Access-point selector
|
||||
* \author Norman Feske
|
||||
* \date 2023-11-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__AP_SELECTOR_WIDGET_H_
|
||||
#define _VIEW__AP_SELECTOR_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <model/wifi_connection.h>
|
||||
#include <model/wpa_passphrase.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Ap_selector_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Ap_selector_widget : Widget<Vbox>
|
||||
{
|
||||
enum class Wlan_config_policy { MANAGED, MANUAL };
|
||||
|
||||
Access_points const &_access_points;
|
||||
Wifi_connection const &_wifi_connection;
|
||||
Wlan_config_policy const &_wlan_config_policy;
|
||||
Blind_wpa_passphrase const &_wpa_passphrase;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void wifi_connect(Access_point::Ssid) = 0;
|
||||
virtual void wifi_disconnect() = 0;
|
||||
};
|
||||
|
||||
/* limit view to highest-quality access points */
|
||||
unsigned const _max_visible_aps = 20;
|
||||
|
||||
Access_point::Bssid _selected { };
|
||||
|
||||
Hosted<Vbox, Action_button> _connect { Id { "Connect" } };
|
||||
|
||||
struct Item : Widget<Hbox>
|
||||
{
|
||||
struct Attr { bool selected; };
|
||||
|
||||
void view(Scope<Hbox> &s, Access_point const &ap, Attr attr) const
|
||||
{
|
||||
bool const hovered = s.hovered();
|
||||
|
||||
s.sub_scope<Left_floating_hbox>([&] (Scope<Hbox, Left_floating_hbox> &s) {
|
||||
s.sub_scope<Icon>("radio", Icon::Attr { .hovered = hovered,
|
||||
.selected = attr.selected });
|
||||
s.sub_scope<Label>(String<20>(" ", ap.ssid));
|
||||
s.sub_scope<Annotation>((ap.protection == Access_point::WPA_PSK)
|
||||
? " (WPA) " : " ");
|
||||
});
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Hbox, Float> &s) {
|
||||
s.attribute("east", "yes");
|
||||
s.sub_scope<Label>(String<8>(ap.quality, "%"));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* \return true if at least one access point fulfils the condition 'cond_fn'
|
||||
*/
|
||||
bool _for_each_ap(auto const &cond_fn) const
|
||||
{
|
||||
bool result = false;
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
result |= cond_fn(ap); });
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _selected_ap_visible() const
|
||||
{
|
||||
unsigned count = 0;
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return (count++ < _max_visible_aps) && (_selected == ap.bssid); });
|
||||
}
|
||||
|
||||
void _with_selected_ap(auto const &fn) const
|
||||
{
|
||||
bool done = false;
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
if (!done && (ap.bssid == _selected)) {
|
||||
fn(ap);
|
||||
done = true; } });
|
||||
|
||||
/*
|
||||
* If access point is not present in the list, fall back to the information
|
||||
* given in the 'state' report.
|
||||
*/
|
||||
if (!done)
|
||||
fn(Access_point { _wifi_connection.bssid,
|
||||
_wifi_connection.ssid,
|
||||
Access_point::UNKNOWN });
|
||||
}
|
||||
|
||||
Ap_selector_widget(Access_points const &aps,
|
||||
Wifi_connection const &wifi_connection,
|
||||
Wlan_config_policy const &wlan_config_policy,
|
||||
Blind_wpa_passphrase const &wpa_passphrase)
|
||||
:
|
||||
_access_points(aps), _wifi_connection(wifi_connection),
|
||||
_wlan_config_policy(wlan_config_policy), _wpa_passphrase(wpa_passphrase)
|
||||
{ }
|
||||
|
||||
void view(Scope<Vbox> &s) const
|
||||
{
|
||||
if (_wlan_config_policy == Wlan_config_policy::MANUAL)
|
||||
return;
|
||||
|
||||
if (_wifi_connection.connecting() || _wifi_connection.connected()) {
|
||||
|
||||
Hosted<Vbox, Item> item { Id { _selected } };
|
||||
|
||||
_with_selected_ap([&] (Access_point const &ap) {
|
||||
s.widget(item, ap, Item::Attr { .selected = true }); });
|
||||
|
||||
s.sub_scope<Label>(_wifi_connection.connecting()
|
||||
? "connecting" : "associated" );
|
||||
return;
|
||||
}
|
||||
|
||||
bool const selected_ap_visible = _selected_ap_visible();
|
||||
|
||||
unsigned count = 0;
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
|
||||
if (count++ >= _max_visible_aps)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Whenever the user has selected an access point, hide all others.
|
||||
* Should the selected AP disappear from the list, show all others.
|
||||
*/
|
||||
bool const selected = (_selected == ap.bssid);
|
||||
if (selected_ap_visible && !selected)
|
||||
return;
|
||||
|
||||
Hosted<Vbox, Item> item { Id { ap.bssid } };
|
||||
s.widget(item, ap, Item::Attr { .selected = selected });
|
||||
|
||||
if (!selected)
|
||||
return;
|
||||
|
||||
bool const connected_to_selected_ap =
|
||||
(selected && _wifi_connection.ssid == ap.ssid)
|
||||
&& _wifi_connection.state == Wifi_connection::CONNECTED;
|
||||
|
||||
if (connected_to_selected_ap)
|
||||
return;
|
||||
|
||||
if (ap.protection == Access_point::WPA_PSK) {
|
||||
s.sub_scope<Label>(_wifi_connection.auth_failure()
|
||||
? "Enter passphrase (auth failure):"
|
||||
: "Enter passphrase:");
|
||||
|
||||
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Frame, Float> &s) {
|
||||
s.attribute("west", "yes");
|
||||
String<3*64> const passphrase(" ", _wpa_passphrase);
|
||||
s.sub_scope<Label>(passphrase, [&] (auto &s) {
|
||||
s.attribute("font", "title/regular");
|
||||
s.sub_node("cursor", [&] {
|
||||
s.attribute("at", passphrase.length() - 1); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (_wpa_passphrase.suitable_for_connect())
|
||||
s.widget(_connect);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Present motivational message until we get the first 'accesspoints'
|
||||
* report.
|
||||
*/
|
||||
if (count == 0)
|
||||
s.sub_scope<Label>("Scanning...");
|
||||
}
|
||||
|
||||
bool need_keyboard_focus_for_passphrase() const
|
||||
{
|
||||
if (_wifi_connection.state == Wifi_connection::CONNECTED
|
||||
|| _wifi_connection.state == Wifi_connection::CONNECTING)
|
||||
return false;
|
||||
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return (_selected == ap.bssid) && ap.wpa_protected(); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Action &action)
|
||||
{
|
||||
Id const ap_id = at.matching_id<Vbox, Item>();
|
||||
|
||||
if (ap_id.valid()) {
|
||||
|
||||
if (ap_id.value == _selected) {
|
||||
action.wifi_disconnect();
|
||||
_selected = { };
|
||||
|
||||
} else {
|
||||
_selected = ap_id.value;
|
||||
|
||||
auto selected_ap_unprotected = [&] {
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return (_selected == ap.bssid) && ap.unprotected(); }); };
|
||||
|
||||
/* immediately connect to unprotected access point when selected */
|
||||
if (selected_ap_unprotected())
|
||||
action.wifi_connect(_selected);
|
||||
}
|
||||
}
|
||||
|
||||
_connect.propagate(at, [&] { action.wifi_connect(_selected); });
|
||||
}
|
||||
|
||||
bool ap_list_shown() const { return !_selected.valid(); }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__AP_SELECTOR_WIDGET_H_ */
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* \brief Debug options dialog
|
||||
* \author Christian Prochaska
|
||||
* \date 2022-09-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DEBUG_DIALOG_H_
|
||||
#define _VIEW__DEBUG_DIALOG_H_
|
||||
|
||||
/* local includes */
|
||||
#include <xml.h>
|
||||
#include <model/component.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Debug_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Debug_dialog : Noncopyable, Deprecated_dialog
|
||||
{
|
||||
bool _monitor = false;
|
||||
bool _wait = false;
|
||||
bool _wx = false;
|
||||
|
||||
Hoverable_item _item { };
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
return Deprecated_dialog::any_hover_changed(
|
||||
_item.match(hover, "vbox", "hbox", "name"));
|
||||
}
|
||||
|
||||
void click(Component &component)
|
||||
{
|
||||
Hoverable_item::Id const clicked = _item._hovered;
|
||||
|
||||
if (!clicked.valid())
|
||||
return;
|
||||
|
||||
if (clicked == "monitor") _monitor = !_monitor;
|
||||
if (clicked == "wx") _wx = !_wx;
|
||||
if (clicked == "wait") _wait = !_wait;
|
||||
|
||||
/* "wx" depends on "monitor", "wait" depends on "wx" */
|
||||
_wx &= _monitor;
|
||||
_wait &= _wx;
|
||||
|
||||
component.wx = _wx;
|
||||
component.monitor = _monitor;
|
||||
component.wait = _wait;
|
||||
}
|
||||
|
||||
void _gen_checkbox(Xml_generator &xml, Start_name const &name,
|
||||
Component::Info const &text, bool selected) const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] {
|
||||
|
||||
gen_named_node(xml, "button", "button", [&] {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", "checkbox");
|
||||
_item.gen_hovered_attr(xml, name);
|
||||
xml.node("hbox", [&] { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] {
|
||||
xml.attribute("text", Path(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] { });
|
||||
});
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
{
|
||||
xml.node("vbox", [&] {
|
||||
_gen_checkbox(xml, "monitor", "Debug", _monitor);
|
||||
|
||||
if (_monitor)
|
||||
_gen_checkbox(xml, "wx", "Allow code patching", _wx);
|
||||
|
||||
if (_wx)
|
||||
_gen_checkbox(xml, "wait", "Wait for GDB", _wait);
|
||||
});
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
_item._hovered = Hoverable_item::Id();
|
||||
_monitor = _wait = _wx = false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DEBUG_DIALOG_H_ */
|
53
repos/gems/src/app/sculpt_manager/view/debug_widget.h
Normal file
53
repos/gems/src/app/sculpt_manager/view/debug_widget.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* \brief Debug-options widget
|
||||
* \author Norman Feske
|
||||
* \date 2023-10-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DEBUG_WIDGET_H_
|
||||
#define _VIEW__DEBUG_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <model/component.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Debug_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Debug_widget : Widget<Vbox>
|
||||
{
|
||||
Hosted<Vbox, Menu_entry> _monitor { Id { "monitor" } },
|
||||
_wx { Id { "wx" } },
|
||||
_wait { Id { "wait" } };
|
||||
|
||||
void click(Clicked_at const &at, Component &component)
|
||||
{
|
||||
_monitor.propagate(at, [&] { component.monitor = !component.monitor; });
|
||||
_wx .propagate(at, [&] { component.wx = !component.wx; });
|
||||
_wait .propagate(at, [&] { component.wait = !component.wait; });
|
||||
|
||||
/* "wx" depends on "monitor", "wait" depends on "wx" */
|
||||
component.wx &= component.monitor;
|
||||
component.wait &= component.wx;
|
||||
}
|
||||
|
||||
void view(Scope<Vbox> &s, Component const &component) const
|
||||
{
|
||||
s.widget(_monitor, component.monitor, "Debug", "checkbox");
|
||||
|
||||
if (component.monitor)
|
||||
s.widget(_wx, component.wx, "Allow code patching", "checkbox");
|
||||
|
||||
if (component.wx)
|
||||
s.widget(_wait, component.wait, "Wait for GDB", "checkbox");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DEBUG_WIDGET_H_ */
|
@ -1,364 +0,0 @@
|
||||
/*
|
||||
* \brief Dialog for selecting a depot user
|
||||
* \author Norman Feske
|
||||
* \date 2023-03-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DEPOT_USERS_DIALOG_H_
|
||||
#define _VIEW__DEPOT_USERS_DIALOG_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
#include <view/text_entry_field.h>
|
||||
#include <model/depot_url.h>
|
||||
|
||||
namespace Sculpt { struct Depot_users_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Depot_users_dialog
|
||||
{
|
||||
public:
|
||||
|
||||
using Depot_users = Attached_rom_dataspace;
|
||||
using User = Depot::Archive::User;
|
||||
using Url = Depot_url::Url;
|
||||
using Hover_result = Hoverable_item::Hover_result;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void add_depot_url(Depot_url const &depot_url) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
using Url_edit_field = Text_entry_field<50>;
|
||||
|
||||
User const _default_user;
|
||||
|
||||
Depot_users const &_depot_users;
|
||||
|
||||
Action &_action;
|
||||
|
||||
User _selected;
|
||||
|
||||
bool _unfolded = false;
|
||||
|
||||
bool _selected_user_exists = false;
|
||||
|
||||
Hoverable_item _user { };
|
||||
Hoverable_item _button { };
|
||||
|
||||
Url const _orig_edit_url { "https://" };
|
||||
|
||||
Url_edit_field _url_edit_field { _orig_edit_url };
|
||||
|
||||
Url _url(Xml_node const &user) const
|
||||
{
|
||||
if (!user.has_sub_node("url"))
|
||||
return { };
|
||||
|
||||
Url const url = user.sub_node("url").decoded_content<Url>();
|
||||
|
||||
/*
|
||||
* Ensure that the URL does not contain any '"' character because
|
||||
* it will be taken as an XML attribute value.
|
||||
*/
|
||||
for (char const *s = url.string(); *s; s++)
|
||||
if (*s == '"')
|
||||
return { };
|
||||
|
||||
User const name = user.attribute_value("name", User());
|
||||
|
||||
return Url(url, "/", name);
|
||||
}
|
||||
|
||||
static void _gen_vspacer(Xml_generator &xml, char const *name)
|
||||
{
|
||||
gen_named_node(xml, "label", name, [&] () {
|
||||
xml.attribute("text", " ");
|
||||
xml.attribute("font", "annotation/regular");
|
||||
});
|
||||
}
|
||||
|
||||
static inline char const *_add_id() { return "/add"; }
|
||||
|
||||
template <typename GEN_LABEL_FN, typename RIGHT_FN>
|
||||
void _gen_item(Xml_generator &xml, User const &name,
|
||||
GEN_LABEL_FN const &gen_label_fn,
|
||||
RIGHT_FN const &right_fn) const
|
||||
{
|
||||
bool const selected = (name == _selected);
|
||||
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "float", "button", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
_user.gen_hovered_attr(xml, name);
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", "radio");
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
gen_label_fn(); });
|
||||
});
|
||||
});
|
||||
gen_named_node(xml, "hbox", "right", [&] () {
|
||||
right_fn(); });
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_entry(Xml_generator &xml, Xml_node const user, bool /* last */) const
|
||||
{
|
||||
User const name = user.attribute_value("name", User());
|
||||
bool const selected = (name == _selected);
|
||||
Url const url = _url(user);
|
||||
Url const label = Depot_url::from_string(url).valid() ? url : Url(name);
|
||||
bool const show_all = _unfolded || !_selected_user_exists;
|
||||
|
||||
if (!selected && !show_all)
|
||||
return;
|
||||
|
||||
_gen_item(xml, name,
|
||||
[&] /* label */ { xml.attribute("text", Path(" ", label)); },
|
||||
[&] /* right */ { }
|
||||
);
|
||||
}
|
||||
|
||||
Depot_url _depot_url(Xml_node const &depot_users) const
|
||||
{
|
||||
Depot_url const result =
|
||||
Depot_url::from_string(Depot_url::Url { _url_edit_field });
|
||||
|
||||
/* check for duplicated user name */
|
||||
bool unique = true;
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
||||
User const name = user.attribute_value("name", User());
|
||||
if (name == result.user)
|
||||
unique = false;
|
||||
});
|
||||
|
||||
return unique ? result : Depot_url { };
|
||||
}
|
||||
|
||||
void _gen_add_entry(Xml_generator &xml, Xml_node const &depot_users) const
|
||||
{
|
||||
_gen_item(xml, _add_id(),
|
||||
|
||||
[&] /* label */ {
|
||||
xml.attribute("text", Depot_url::Url(" ", _url_edit_field));
|
||||
xml.attribute("min_ex", 30);
|
||||
xml.node("cursor", [&] () {
|
||||
xml.attribute("at", _url_edit_field.cursor_pos + 1); });
|
||||
},
|
||||
|
||||
[&] /* right */ {
|
||||
gen_named_node(xml, "float", "actions", [&] {
|
||||
xml.attribute("east", "yes");
|
||||
bool const editing = (_selected == _add_id());
|
||||
if (editing) {
|
||||
bool const url_valid = _depot_url(depot_users).valid();
|
||||
gen_named_node(xml, "button", "add", [&] {
|
||||
if (!url_valid)
|
||||
xml.attribute("style", "unimportant");
|
||||
else
|
||||
_button.gen_hovered_attr(xml, "add");
|
||||
|
||||
xml.node("label", [&] {
|
||||
if (!url_valid)
|
||||
xml.attribute("style", "unimportant");
|
||||
xml.attribute("text", "Add"); });
|
||||
});
|
||||
} else {
|
||||
gen_named_node(xml, "button", "edit", [&] {
|
||||
_button.gen_hovered_attr(xml, "edit");
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("text", "Edit"); }); });
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void _gen_selection(Xml_generator &xml) const
|
||||
{
|
||||
Xml_node const depot_users = _depot_users.xml();
|
||||
|
||||
size_t remain_count = depot_users.num_sub_nodes();
|
||||
|
||||
remain_count++; /* account for '_gen_add_entry' */
|
||||
|
||||
bool known_pubkey = false;
|
||||
|
||||
gen_named_node(xml, "frame", "user_selection", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
||||
|
||||
if (_selected == user.attribute_value("name", User()))
|
||||
known_pubkey = user.attribute_value("known_pubkey", false);
|
||||
|
||||
bool const last = (--remain_count == 0);
|
||||
_gen_entry(xml, user, last); });
|
||||
|
||||
if (_unfolded || !_selected_user_exists)
|
||||
_gen_add_entry(xml, depot_users);
|
||||
});
|
||||
});
|
||||
|
||||
if (!_unfolded && !known_pubkey && _selected_user_exists) {
|
||||
gen_named_node(xml, "button", "pubkey warning", [&] {
|
||||
xml.attribute("style", "invisible");
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("font", "annotation/regular");
|
||||
xml.attribute("text", "missing public key for verification"); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Depot_users_dialog(Depot_users const &depot_users,
|
||||
User const &default_user,
|
||||
Action &action)
|
||||
:
|
||||
_default_user(default_user), _depot_users(depot_users),
|
||||
_action(action), _selected(default_user)
|
||||
{ }
|
||||
|
||||
User selected() const
|
||||
{
|
||||
return (_selected == _add_id()) ? User() : _selected;
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const { _gen_selection(xml); }
|
||||
|
||||
bool unfolded() const { return _unfolded || !_selected_user_exists; }
|
||||
|
||||
struct User_properties
|
||||
{
|
||||
bool exists;
|
||||
bool download_url;
|
||||
bool public_key;
|
||||
};
|
||||
|
||||
User_properties selected_user_properties() const
|
||||
{
|
||||
User_properties result { };
|
||||
_depot_users.xml().for_each_sub_node([&] (Xml_node const &user) {
|
||||
if (_selected == user.attribute_value("name", User())) {
|
||||
result = {
|
||||
.exists = true,
|
||||
.download_url = Depot_url::from_string(_url(user)).valid(),
|
||||
.public_key = user.attribute_value("known_pubkey", false)
|
||||
};
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename SELECT_FN>
|
||||
void click(SELECT_FN const &select_fn)
|
||||
{
|
||||
/* unfold depot users */
|
||||
if (!_unfolded) {
|
||||
_unfolded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle click on unfolded depot-user selection */
|
||||
|
||||
auto select_depot_user = [&] (User const &user)
|
||||
{
|
||||
_selected = user;
|
||||
select_fn(user);
|
||||
_unfolded = false;
|
||||
_selected_user_exists = true;
|
||||
_url_edit_field = _orig_edit_url;
|
||||
};
|
||||
|
||||
if (_user._hovered.length() <= 1)
|
||||
return;
|
||||
|
||||
if (_user.hovered(_add_id())) {
|
||||
if (_button.hovered("add")) {
|
||||
Depot_url const depot_url = _depot_url(_depot_users.xml());
|
||||
if (depot_url.valid()) {
|
||||
_action.add_depot_url(depot_url);
|
||||
select_depot_user(depot_url.user);
|
||||
}
|
||||
} else {
|
||||
_selected = _add_id();
|
||||
}
|
||||
} else {
|
||||
select_depot_user(_user._hovered);
|
||||
}
|
||||
}
|
||||
|
||||
Hover_result hover(Xml_node const &hover)
|
||||
{
|
||||
return Deprecated_dialog::any_hover_changed(
|
||||
_user .match(hover, "frame", "vbox", "hbox", "name"),
|
||||
_button.match(hover, "frame", "vbox", "hbox", "hbox", "float", "button", "name")
|
||||
);
|
||||
}
|
||||
|
||||
void reset_hover() { _user._hovered = { }; }
|
||||
|
||||
bool hovered() const { return _user._hovered.valid(); }
|
||||
|
||||
bool keyboard_needed() const { return _selected == _add_id(); }
|
||||
|
||||
void handle_key(Codepoint c)
|
||||
{
|
||||
if (_selected != _add_id())
|
||||
return;
|
||||
|
||||
/* prevent input of printable yet risky characters as URL */
|
||||
if (c.value == ' ' || c.value == '"')
|
||||
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);
|
||||
}
|
||||
|
||||
bool one_selected() const { return !_unfolded && _selected.length() > 1; }
|
||||
|
||||
void sanitize_unfold_state()
|
||||
{
|
||||
/*
|
||||
* If the selected depot user does not exist in the depot, show
|
||||
* list of available users.
|
||||
*/
|
||||
_selected_user_exists = false;
|
||||
|
||||
_depot_users.xml().for_each_sub_node([&] (Xml_node const &user) {
|
||||
if (_selected == user.attribute_value("name", User()))
|
||||
_selected_user_exists = true; });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DEPOT_USERS_DIALOG_H_ */
|
346
repos/gems/src/app/sculpt_manager/view/depot_users_widget.h
Normal file
346
repos/gems/src/app/sculpt_manager/view/depot_users_widget.h
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* \brief Widget for selecting a depot user
|
||||
* \author Norman Feske
|
||||
* \date 2023-03-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DEPOT_USERS_WIDGET_H_
|
||||
#define _VIEW__DEPOT_USERS_WIDGET_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
#include <view/text_entry_field.h>
|
||||
#include <model/depot_url.h>
|
||||
|
||||
namespace Sculpt { struct Depot_users_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Depot_users_widget : Widget<Vbox>
|
||||
{
|
||||
public:
|
||||
|
||||
using Depot_users = Attached_rom_dataspace;
|
||||
using User = Depot::Archive::User;
|
||||
using Url = Depot_url::Url;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void add_depot_url(Depot_url const &depot_url) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
using Url_edit_field = Text_entry_field<50>;
|
||||
|
||||
User const _default_user;
|
||||
|
||||
Depot_users const &_depot_users;
|
||||
|
||||
User _selected { _default_user };
|
||||
|
||||
bool _unfolded = false;
|
||||
|
||||
bool _selected_user_exists = false;
|
||||
|
||||
Url _url(Xml_node const &user) const
|
||||
{
|
||||
if (!user.has_sub_node("url"))
|
||||
return { };
|
||||
|
||||
Url const url = user.sub_node("url").decoded_content<Url>();
|
||||
|
||||
/*
|
||||
* Ensure that the URL does not contain any '"' character because
|
||||
* it will be taken as an XML attribute value.
|
||||
*/
|
||||
for (char const *s = url.string(); *s; s++)
|
||||
if (*s == '"')
|
||||
return { };
|
||||
|
||||
User const name = user.attribute_value("name", User());
|
||||
|
||||
return Url(url, "/", name);
|
||||
}
|
||||
|
||||
static inline char const *_add_id() { return "/add"; }
|
||||
|
||||
struct Conditional_button : Widget<Button>
|
||||
{
|
||||
Dialog::Event::Seq_number _seq_number { };
|
||||
|
||||
void view(Scope<Button> &s, bool ready, auto const &text) const
|
||||
{
|
||||
bool const selected = _seq_number == s.hover.seq_number;
|
||||
|
||||
if (!ready)
|
||||
s.attribute("style", "unimportant");
|
||||
|
||||
if (selected)
|
||||
s.attribute("selected", "yes");
|
||||
|
||||
if (s.hovered() && !s.dragged() && !selected && ready)
|
||||
s.attribute("hovered", "yes");
|
||||
|
||||
s.sub_scope<Label>(text, [&] (auto &s) {
|
||||
if (!ready)
|
||||
s.attribute("style", "unimportant"); });
|
||||
}
|
||||
|
||||
void view(Scope<Button> &s, bool ready) const
|
||||
{
|
||||
view(s, ready, s.id.value);
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, auto const &fn)
|
||||
{
|
||||
_seq_number = at.seq_number;
|
||||
fn();
|
||||
}
|
||||
};
|
||||
|
||||
struct Item : Widget<Hbox>
|
||||
{
|
||||
void view(Scope<Hbox> &s, bool selected, auto const &text) const
|
||||
{
|
||||
bool const hovered = s.hovered();
|
||||
|
||||
s.sub_scope<Left_floating_hbox>([&] (Scope<Hbox, Left_floating_hbox> &s) {
|
||||
s.sub_scope<Icon>("radio", Icon::Attr { .hovered = hovered,
|
||||
.selected = selected });
|
||||
s.sub_scope<Label>(text);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct Edit_item : Widget<Hbox>
|
||||
{
|
||||
Url const _orig_edit_url { "https://" };
|
||||
|
||||
Url_edit_field _url_edit_field { _orig_edit_url };
|
||||
|
||||
Depot_url depot_url(Xml_node const &depot_users) const
|
||||
{
|
||||
Depot_url const result =
|
||||
Depot_url::from_string(Depot_url::Url { _url_edit_field });
|
||||
|
||||
/* check for duplicated user name */
|
||||
bool unique = true;
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
||||
User const name = user.attribute_value("name", User());
|
||||
if (name == result.user)
|
||||
unique = false; });
|
||||
|
||||
return unique ? result : Depot_url { };
|
||||
}
|
||||
|
||||
Hosted<Hbox, Hbox, Float, Conditional_button> _add { Id { "Add" } };
|
||||
Hosted<Hbox, Hbox, Float, Action_button> _edit { Id { "Edit" } };
|
||||
|
||||
bool _ready_to_add(Xml_node const &depot_users) const
|
||||
{
|
||||
return depot_url(depot_users).valid();
|
||||
}
|
||||
|
||||
void view(Scope<Hbox> &s, bool selected, Xml_node const &depot_users) const
|
||||
{
|
||||
bool const hovered = s.hovered() && !s.dragged() && !selected;
|
||||
|
||||
s.sub_scope<Left_floating_hbox>([&] (Scope<Hbox, Left_floating_hbox> &s) {
|
||||
|
||||
s.sub_scope<Icon>("radio", Icon::Attr { .hovered = hovered,
|
||||
.selected = selected });
|
||||
|
||||
auto const text = Depot_url::Url(" ", _url_edit_field);
|
||||
s.sub_scope<Label>(text, [&] (auto &s) {
|
||||
s.attribute("min_ex", 30);
|
||||
s.template sub_node("cursor", [&] {
|
||||
s.attribute("at", _url_edit_field.cursor_pos + 1); });
|
||||
});
|
||||
});
|
||||
|
||||
s.sub_scope<Hbox>([&] (Scope<Hbox, Hbox> &s) {
|
||||
s.sub_scope<Float>([&] (Scope<Hbox, Hbox, Float> &s) {
|
||||
s.attribute("east", "yes");
|
||||
if (selected)
|
||||
s.widget(_add, _ready_to_add(depot_users));
|
||||
else
|
||||
s.widget(_edit);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
_url_edit_field = _orig_edit_url;
|
||||
}
|
||||
|
||||
void handle_key(Codepoint c, auto const &enter_fn)
|
||||
{
|
||||
/* prevent input of printable yet risky characters as URL */
|
||||
if (c.value == ' ' || c.value == '"')
|
||||
return;
|
||||
|
||||
/* respond to enter key */
|
||||
if (c.value == 10)
|
||||
enter_fn();
|
||||
|
||||
_url_edit_field.apply(c);
|
||||
}
|
||||
|
||||
void click(Clicked_at const &, Xml_node const &depot_users, auto const add_fn)
|
||||
{
|
||||
if (_ready_to_add(depot_users))
|
||||
add_fn();
|
||||
}
|
||||
};
|
||||
|
||||
Hosted<Vbox, Frame, Vbox, Edit_item> _edit_item { Id { _add_id() } };
|
||||
|
||||
public:
|
||||
|
||||
Depot_users_widget(Depot_users const &depot_users,
|
||||
User const &default_user)
|
||||
:
|
||||
_default_user(default_user), _depot_users(depot_users)
|
||||
{ }
|
||||
|
||||
User selected() const
|
||||
{
|
||||
return (_selected == _add_id()) ? User() : _selected;
|
||||
}
|
||||
|
||||
void view(Scope<Vbox> &s) const
|
||||
{
|
||||
Xml_node const depot_users = _depot_users.xml();
|
||||
|
||||
bool known_pubkey = false;
|
||||
|
||||
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node const &user) {
|
||||
|
||||
if (_selected == user.attribute_value("name", User()))
|
||||
known_pubkey = user.attribute_value("known_pubkey", false);
|
||||
|
||||
User const name = user.attribute_value("name", User());
|
||||
bool const selected = (name == _selected);
|
||||
Url const url = _url(user);
|
||||
Url const label = Depot_url::from_string(url).valid() ? url : Url(name);
|
||||
bool const show_all = _unfolded || !_selected_user_exists;
|
||||
|
||||
if (!selected && !show_all)
|
||||
return;
|
||||
|
||||
Hosted<Vbox, Frame, Vbox, Item> item { Id { name } };
|
||||
|
||||
s.widget(item, selected, Path(" ", label));
|
||||
});
|
||||
|
||||
if (_unfolded || !_selected_user_exists)
|
||||
s.widget(_edit_item, (_selected == _add_id()), depot_users);
|
||||
});
|
||||
});
|
||||
|
||||
if (!_unfolded && !known_pubkey && _selected_user_exists) {
|
||||
s.sub_scope<Button>([&] (Scope<Vbox, Button> &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_scope<Annotation>("missing public key for verification");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool unfolded() const { return _unfolded || !_selected_user_exists; }
|
||||
|
||||
struct User_properties
|
||||
{
|
||||
bool exists;
|
||||
bool download_url;
|
||||
bool public_key;
|
||||
};
|
||||
|
||||
User_properties selected_user_properties() const
|
||||
{
|
||||
User_properties result { };
|
||||
_depot_users.xml().for_each_sub_node([&] (Xml_node const &user) {
|
||||
if (_selected == user.attribute_value("name", User())) {
|
||||
result = {
|
||||
.exists = true,
|
||||
.download_url = Depot_url::from_string(_url(user)).valid(),
|
||||
.public_key = user.attribute_value("known_pubkey", false)
|
||||
};
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void _select_depot_user(User const &user)
|
||||
{
|
||||
_selected = user;
|
||||
_unfolded = false;
|
||||
_selected_user_exists = true;
|
||||
_edit_item.reset();
|
||||
}
|
||||
|
||||
void _add_and_select_new_depot_user(Action &action)
|
||||
{
|
||||
Depot_url const depot_url = _edit_item.depot_url(_depot_users.xml());
|
||||
if (depot_url.valid()) {
|
||||
action.add_depot_url(depot_url);
|
||||
_select_depot_user(depot_url.user);
|
||||
}
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Action &action, auto const &select_fn)
|
||||
{
|
||||
/* unfold depot users */
|
||||
if (!_unfolded) {
|
||||
_unfolded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Id const item = at.matching_id<Vbox, Frame, Vbox, Item>();
|
||||
if (item.valid()) {
|
||||
if (item.value == _add_id()) {
|
||||
_selected = item.value;
|
||||
} else {
|
||||
_select_depot_user(item.value);
|
||||
select_fn(item.value);
|
||||
}
|
||||
}
|
||||
|
||||
_edit_item.propagate(at, _depot_users.xml(), [&] {
|
||||
_add_and_select_new_depot_user(action); });
|
||||
}
|
||||
|
||||
bool keyboard_needed() const { return _selected == _add_id(); }
|
||||
|
||||
void handle_key(Codepoint c, Action &action)
|
||||
{
|
||||
if (_selected == _add_id())
|
||||
_edit_item.handle_key(c, [&] {
|
||||
_add_and_select_new_depot_user(action); });
|
||||
}
|
||||
|
||||
bool one_selected() const { return !_unfolded && _selected.length() > 1; }
|
||||
|
||||
void sanitize_unfold_state()
|
||||
{
|
||||
/*
|
||||
* If the selected depot user does not exist in the depot, show
|
||||
* list of available users.
|
||||
*/
|
||||
_selected_user_exists = false;
|
||||
|
||||
_depot_users.xml().for_each_sub_node([&] (Xml_node const &user) {
|
||||
if (_selected == user.attribute_value("name", User()))
|
||||
_selected_user_exists = true; });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DEPOT_USERS_WIDGET_H_ */
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* \brief Menu-view dialog handling
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DEPRECATED_DIALOG_H_
|
||||
#define _VIEW__DEPRECATED_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <input/event.h>
|
||||
#include <util/xml_generator.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
#include <view/hoverable_item.h>
|
||||
|
||||
namespace Sculpt { struct Deprecated_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Deprecated_dialog : Interface
|
||||
{
|
||||
bool hovered = false;
|
||||
|
||||
enum class Click_result { CONSUMED, IGNORED };
|
||||
enum class Clack_result { CONSUMED, IGNORED };
|
||||
|
||||
using Hover_result = Hoverable_item::Hover_result;
|
||||
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual Hover_result hover(Xml_node hover) = 0;
|
||||
|
||||
virtual void generate(Xml_generator &xml) const = 0;
|
||||
|
||||
static Hover_result any_hover_changed(Hover_result head)
|
||||
{
|
||||
return head;
|
||||
}
|
||||
|
||||
template <typename... TAIL>
|
||||
static Hover_result any_hover_changed(Hover_result head, TAIL... args)
|
||||
{
|
||||
if (head == Hover_result::CHANGED)
|
||||
return Hover_result::CHANGED;
|
||||
|
||||
return any_hover_changed(args...);
|
||||
}
|
||||
|
||||
Hover_result _match_sub_dialog(Xml_node node)
|
||||
{
|
||||
hovered = true;
|
||||
return hover(node);
|
||||
}
|
||||
|
||||
template <typename... TAIL>
|
||||
Hover_result _match_sub_dialog(Xml_node hover, char const *sub_node,
|
||||
TAIL &&... tail)
|
||||
{
|
||||
Hover_result result = Hover_result::UNMODIFIED;
|
||||
|
||||
hover.with_optional_sub_node(sub_node, [&] (Xml_node sub_hover) {
|
||||
if (_match_sub_dialog(sub_hover, tail...) == Hover_result::CHANGED)
|
||||
result = Hover_result::CHANGED; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... ARGS>
|
||||
Hover_result match_sub_dialog(ARGS &&... args)
|
||||
{
|
||||
bool const orig_hovered = hovered;
|
||||
|
||||
hovered = false;
|
||||
|
||||
Hover_result const result = _match_sub_dialog(args...);
|
||||
|
||||
/* reset internal hover state of the dialog if no longer hovered */
|
||||
if (orig_hovered && !hovered)
|
||||
(void)this->hover(Xml_node("<empty/>"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpe for the 'Sculpt::match_sub_dialog' implementation below
|
||||
*/
|
||||
template <typename DIALOG>
|
||||
struct _Recurse
|
||||
{
|
||||
DIALOG &dialog;
|
||||
|
||||
Hover_result result = Hover_result::UNMODIFIED;
|
||||
|
||||
_Recurse(DIALOG &dialog) : dialog(dialog) { }
|
||||
|
||||
template <typename... TAIL>
|
||||
void match(Xml_node const &hover, char const *head, TAIL &&... tail)
|
||||
{
|
||||
hover.with_optional_sub_node(head, [&] (Xml_node const &sub_node) {
|
||||
match(sub_node, tail...); });
|
||||
}
|
||||
|
||||
void match(Xml_node const &hover) { result = dialog.hover(hover); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
namespace Sculpt {
|
||||
|
||||
template <typename DIALOG, typename... ARGS>
|
||||
static inline Hoverable_item::Hover_result
|
||||
match_sub_dialog(Xml_node const &hover, DIALOG &dialog, ARGS &&... args)
|
||||
{
|
||||
Deprecated_dialog::_Recurse recurse { dialog };
|
||||
|
||||
recurse.match(hover, args...);
|
||||
|
||||
static Xml_node const unhovered { "<empty/>" };
|
||||
|
||||
if (recurse.result == Hoverable_item::Hover_result::UNMODIFIED)
|
||||
dialog.hover(unhovered);
|
||||
|
||||
return recurse.result;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _VIEW__DEPRECATED_DIALOG_H_ */
|
@ -26,34 +26,47 @@ namespace Dialog {
|
||||
struct Left_right_annotation;
|
||||
struct Left_floating_text;
|
||||
struct Left_floating_hbox;
|
||||
struct Top_left_floating_hbox;
|
||||
struct Right_floating_hbox;
|
||||
struct Vgap;
|
||||
struct Small_vgap;
|
||||
struct Button_vgap;
|
||||
struct Centered_info_vbox;
|
||||
struct Centered_dialog_vbox;
|
||||
struct Icon;
|
||||
struct Titled_frame;
|
||||
struct Pin_button;
|
||||
struct Pin_row;
|
||||
struct Menu_entry;
|
||||
struct Operation_button;
|
||||
struct Right_floating_off_on;
|
||||
struct Doublechecked_action_button;
|
||||
template <typename> struct Radio_select_button;
|
||||
template <typename> struct Choice;
|
||||
}
|
||||
|
||||
|
||||
struct Dialog::Annotation : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename TEXT>
|
||||
static void sub_node(SCOPE &s, TEXT const &text)
|
||||
static void sub_node(auto &scope, auto const &text)
|
||||
{
|
||||
s.sub_node("label", [&] {
|
||||
s.attribute("text", text);
|
||||
s.attribute("font", "annotation/regular"); });
|
||||
scope.sub_node("label", [&] {
|
||||
scope.attribute("text", text);
|
||||
scope.attribute("font", "annotation/regular"); });
|
||||
}
|
||||
|
||||
static void view_sub_scope(auto &scope, auto const &text)
|
||||
{
|
||||
sub_node(scope, text);
|
||||
}
|
||||
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Left_annotation : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename TEXT>
|
||||
static void view_sub_scope(SCOPE &s, TEXT const &text)
|
||||
static void view_sub_scope(auto &s, auto const &text)
|
||||
{
|
||||
s.node("hbox", [&] {
|
||||
s.sub_node("float", [&] () {
|
||||
@ -61,15 +74,13 @@ struct Dialog::Left_annotation : Sub_scope
|
||||
Annotation::sub_node(s, text); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &, FN const &) { }
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Left_right_annotation : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename LEFT_TEXT, typename RIGHT_TEXT>
|
||||
static void view_sub_scope(SCOPE &s, LEFT_TEXT const &left, RIGHT_TEXT const &right)
|
||||
static void view_sub_scope(auto &s, auto const &left, auto const &right)
|
||||
{
|
||||
s.node("hbox", [&] {
|
||||
s.named_sub_node("float", "left", [&] () {
|
||||
@ -82,15 +93,13 @@ struct Dialog::Left_right_annotation : Sub_scope
|
||||
});
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &, FN const &) { }
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Left_floating_text : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename TEXT>
|
||||
static void view_sub_scope(SCOPE &s, TEXT const &text)
|
||||
static void view_sub_scope(auto &s, auto const &text)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.attribute("west", "yes");
|
||||
@ -99,15 +108,13 @@ struct Dialog::Left_floating_text : Sub_scope
|
||||
s.attribute("min_ex", "15"); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &, FN const &) { }
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Left_floating_hbox : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename FN>
|
||||
static void view_sub_scope(SCOPE &s, FN const &fn)
|
||||
static void view_sub_scope(auto &s, auto const &fn)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.attribute("west", "yes");
|
||||
@ -115,10 +122,28 @@ struct Dialog::Left_floating_hbox : Sub_scope
|
||||
fn(s); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &at, FN const &fn)
|
||||
static void with_narrowed_at(auto const &at, auto const &fn)
|
||||
{
|
||||
with_narrowed_xml(at, "float", [&] (AT const &at) {
|
||||
with_narrowed_xml(at, "float", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "hbox", fn); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Top_left_floating_hbox : Sub_scope
|
||||
{
|
||||
static void view_sub_scope(auto &s, auto const &fn)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.attribute("west", "yes");
|
||||
s.attribute("north", "yes");
|
||||
s.named_sub_node("hbox", s.id.value, [&] {
|
||||
fn(s); }); });
|
||||
}
|
||||
|
||||
static void with_narrowed_at(auto const &at, auto const &fn)
|
||||
{
|
||||
with_narrowed_xml(at, "float", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "hbox", fn); });
|
||||
}
|
||||
};
|
||||
@ -126,8 +151,7 @@ struct Dialog::Left_floating_hbox : Sub_scope
|
||||
|
||||
struct Dialog::Right_floating_hbox : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename FN>
|
||||
static void view_sub_scope(SCOPE &s, FN const &fn)
|
||||
static void view_sub_scope(auto &s, auto const &fn)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.attribute("east", "yes");
|
||||
@ -135,10 +159,9 @@ struct Dialog::Right_floating_hbox : Sub_scope
|
||||
fn(s); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &at, FN const &fn)
|
||||
static void with_narrowed_at(auto const &at, auto const &fn)
|
||||
{
|
||||
with_narrowed_xml(at, "float", [&] (AT const &at) {
|
||||
with_narrowed_xml(at, "float", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "hbox", fn); });
|
||||
}
|
||||
};
|
||||
@ -146,21 +169,45 @@ struct Dialog::Right_floating_hbox : Sub_scope
|
||||
|
||||
struct Dialog::Vgap : Sub_scope
|
||||
{
|
||||
template <typename SCOPE>
|
||||
static void view_sub_scope(SCOPE &s)
|
||||
static void view_sub_scope(auto &s)
|
||||
{
|
||||
s.node("label", [&] { s.attribute("text", " "); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &, FN const &) { }
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Small_vgap : Sub_scope
|
||||
{
|
||||
static void view_sub_scope(auto &s)
|
||||
{
|
||||
s.node("label", [&] {
|
||||
s.attribute("text", "");
|
||||
s.attribute("font", "annotation/regular"); });
|
||||
}
|
||||
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Button_vgap : Sub_scope
|
||||
{
|
||||
static void view_sub_scope(auto &s)
|
||||
{
|
||||
/* inflate vertical space to button size */
|
||||
s.node("button", [&] {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_node("label", [&] { s.attribute("text", ""); }); });
|
||||
}
|
||||
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Centered_info_vbox : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename FN>
|
||||
static void view_sub_scope(SCOPE &s, FN const &fn)
|
||||
static void view_sub_scope(auto &s, auto const &fn)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.sub_node("frame", [&] {
|
||||
@ -169,15 +216,13 @@ struct Dialog::Centered_info_vbox : Sub_scope
|
||||
fn(s); }); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &, FN const &) { }
|
||||
static void with_narrowed_at(auto const &, auto const &) { }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Centered_dialog_vbox : Sub_scope
|
||||
{
|
||||
template <typename SCOPE, typename FN>
|
||||
static void view_sub_scope(SCOPE &s, FN const &fn)
|
||||
static void view_sub_scope(auto &s, auto const &fn)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.sub_node("frame", [&] {
|
||||
@ -186,22 +231,50 @@ struct Dialog::Centered_dialog_vbox : Sub_scope
|
||||
fn(s); }); }); });
|
||||
}
|
||||
|
||||
template <typename AT, typename FN>
|
||||
static void with_narrowed_at(AT const &at, FN const &fn)
|
||||
static void with_narrowed_at(auto const &at, auto const &fn)
|
||||
{
|
||||
with_narrowed_xml(at, "float", [&] (AT const &at) {
|
||||
with_narrowed_xml(at, "frame", [&] (AT const &at) {
|
||||
with_narrowed_xml(at, "float", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "frame", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "vbox", fn); }); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Icon : Sub_scope
|
||||
{
|
||||
struct Attr { bool hovered, selected; };
|
||||
|
||||
/* used whenever the icon's hover sensitivity is larger than the icon */
|
||||
static void view_sub_scope(auto &s, auto const &style, Attr attr)
|
||||
{
|
||||
s.node("float", [&] {
|
||||
s.sub_node("button", [&] {
|
||||
s.attribute("style", style);
|
||||
if (attr.selected) s.attribute("selected", "yes");
|
||||
if (attr.hovered) s.attribute("hovered", "yes");
|
||||
s.sub_node("hbox", [&] { }); }); });
|
||||
}
|
||||
|
||||
/* used when hovering responds only to the icon's boundaries */
|
||||
static void view_sub_scope(auto &s, auto const &style, bool selected)
|
||||
{
|
||||
view_sub_scope(s, style, Attr { .hovered = s.hovered(),
|
||||
.selected = selected });
|
||||
}
|
||||
|
||||
static void with_narrowed_at(auto const &at, auto const &fn)
|
||||
{
|
||||
with_narrowed_xml(at, "float", [&] (auto const &at) {
|
||||
with_narrowed_xml(at, "button", fn); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Titled_frame : Widget<Frame>
|
||||
{
|
||||
struct Attr { unsigned min_ex; };
|
||||
|
||||
template <typename FN>
|
||||
static void view(Scope<Frame> &s, Attr const attr, FN const &fn)
|
||||
static void view(Scope<Frame> &s, Attr const attr, auto const &fn)
|
||||
{
|
||||
s.sub_node("vbox", [&] {
|
||||
if (attr.min_ex)
|
||||
@ -213,8 +286,7 @@ struct Dialog::Titled_frame : Widget<Frame>
|
||||
fn(); }); }); });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
static void view(Scope<Frame> &s, FN const &fn)
|
||||
static void view(Scope<Frame> &s, auto const &fn)
|
||||
{
|
||||
view(s, Attr { }, fn);
|
||||
}
|
||||
@ -228,31 +300,23 @@ struct Dialog::Radio_select_button : Widget<Left_floating_hbox>
|
||||
|
||||
Radio_select_button(ENUM value) : value(value) { }
|
||||
|
||||
template <typename TEXT>
|
||||
void view(Scope<Left_floating_hbox> &s, ENUM const &selected_value, TEXT const &text) const
|
||||
void view(Scope<Left_floating_hbox> &s, ENUM const &selected_value, auto const &text) const
|
||||
{
|
||||
bool const selected = (selected_value == value),
|
||||
hovered = (s.hovered() && !s.dragged() && !selected);
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Left_floating_hbox, Float> &s) {
|
||||
s.sub_scope<Button>([&] (Scope<Left_floating_hbox, Float, Button> &s) {
|
||||
s.attribute("style", "radio");
|
||||
|
||||
if (selected) s.attribute("selected", "yes");
|
||||
if (hovered) s.attribute("hovered", "yes");
|
||||
|
||||
s.sub_scope<Hbox>();
|
||||
});
|
||||
});
|
||||
|
||||
/* inflate vertical space to button size */
|
||||
s.sub_scope<Button>([&] (Scope<Left_floating_hbox, Button> &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_scope<Label>(text); });
|
||||
s.sub_scope<Icon>("radio", Icon::Attr { .hovered = hovered,
|
||||
.selected = selected });
|
||||
s.sub_scope<Label>(String<100>(" ", text));
|
||||
s.sub_scope<Button_vgap>();
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void click(Clicked_at const &, FN const &fn) { fn(value); }
|
||||
void view(Scope<Left_floating_hbox> &s, ENUM const &selected_value) const
|
||||
{
|
||||
view(s, selected_value, s.id.value);
|
||||
}
|
||||
|
||||
void click(Clicked_at const &, auto const &fn) const { fn(value); }
|
||||
};
|
||||
|
||||
|
||||
@ -275,7 +339,7 @@ struct Dialog::Pin_button : Action_button
|
||||
s.sub_scope<Vbox>([&] (Scope<Button, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(10);
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Dialog::Label>(text, [&] (auto &s) {
|
||||
s.sub_scope<Label>(text, [&] (auto &s) {
|
||||
if (!attr.visible)
|
||||
s.attribute("style", "invisible");
|
||||
s.attribute("font", "title/regular"); });
|
||||
@ -289,8 +353,7 @@ struct Dialog::Pin_row : Widget<Hbox>
|
||||
{
|
||||
Hosted<Hbox, Pin_button> _buttons[3];
|
||||
|
||||
template <typename S1, typename S2, typename S3>
|
||||
Pin_row(S1 const &left, S2 const &middle, S3 const &right)
|
||||
Pin_row(auto const &left, auto const &middle, auto const &right)
|
||||
:
|
||||
_buttons { Id { left }, Id { middle }, Id { right } }
|
||||
{ }
|
||||
@ -304,8 +367,7 @@ struct Dialog::Pin_row : Widget<Hbox>
|
||||
s.widget(_buttons[2], Pin_button::Attr { visible.right });
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void click(Clicked_at const &at, FN const &fn)
|
||||
void click(Clicked_at const &at, auto const &fn)
|
||||
{
|
||||
for (auto &button : _buttons)
|
||||
button.propagate(at, [&] { fn(button.id.value); });
|
||||
@ -313,6 +375,83 @@ struct Dialog::Pin_row : Widget<Hbox>
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Menu_entry : Widget<Left_floating_hbox>
|
||||
{
|
||||
void view(Scope<Left_floating_hbox> &s, bool selected, auto const &text,
|
||||
char const * const style = "radio") const
|
||||
{
|
||||
bool const hovered = (s.hovered() && !s.dragged());
|
||||
|
||||
s.sub_scope<Icon>(style, Icon::Attr { .hovered = hovered,
|
||||
.selected = selected });
|
||||
s.sub_scope<Label>(String<100>(" ", text));
|
||||
s.sub_scope<Button_vgap>();
|
||||
}
|
||||
|
||||
void click(Clicked_at const &, auto const &fn) const { fn(); }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Operation_button : Widget<Button>
|
||||
{
|
||||
void view(Scope<Button> &s, bool selected, auto const &text) const
|
||||
{
|
||||
if (selected) {
|
||||
s.attribute("selected", "yes");
|
||||
s.attribute("style", "unimportant");
|
||||
}
|
||||
|
||||
if (s.hovered() && !s.dragged() && !selected)
|
||||
s.attribute("hovered", "yes");
|
||||
|
||||
s.sub_scope<Label>(String<50>(" ", text, " "));
|
||||
}
|
||||
|
||||
void view(Scope<Button> &s, bool selected) const
|
||||
{
|
||||
view(s, selected, s.id.value);
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void click(Clicked_at const &, FN const &fn) const { fn(); }
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Right_floating_off_on : Widget<Right_floating_hbox>
|
||||
{
|
||||
struct Attr { bool on, transient; };
|
||||
|
||||
Hosted<Right_floating_hbox, Select_button<bool>> const
|
||||
_off { Id { " Off " }, false },
|
||||
_on { Id { " On " }, true };
|
||||
|
||||
void view(Scope<Right_floating_hbox> &s, Attr attr) const
|
||||
{
|
||||
auto transient_attr_fn = [&] (Scope<Button> &s)
|
||||
{
|
||||
if (attr.transient)
|
||||
s.attribute("style", "unimportant");
|
||||
|
||||
s.sub_scope<Label>(s.id.value);
|
||||
};
|
||||
|
||||
s.widget(_off, attr.on, transient_attr_fn);
|
||||
s.widget(_on, attr.on, transient_attr_fn);
|
||||
}
|
||||
|
||||
void view(Scope<Right_floating_hbox> &s, bool on) const
|
||||
{
|
||||
view(s, Attr { .on = on, .transient = false });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, auto const &fn) const
|
||||
{
|
||||
_off.propagate(at, [&] (bool) { fn(false); });
|
||||
_on .propagate(at, [&] (bool) { fn(true); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dialog::Doublechecked_action_button
|
||||
{
|
||||
bool selected = false;
|
||||
@ -329,16 +468,15 @@ struct Dialog::Doublechecked_action_button
|
||||
|
||||
void reset() { selected = false, confirmed = false; }
|
||||
|
||||
template <typename TEXT>
|
||||
void view(Scope<Vbox> &s, TEXT const &text) const
|
||||
void view(Scope<Vbox> &s, auto const &text) const
|
||||
{
|
||||
s.widget(_operation, selected, [&] (Scope<Button> &s) {
|
||||
s.sub_scope<Dialog::Label>(text); });
|
||||
s.sub_scope<Label>(text); });
|
||||
|
||||
if (selected)
|
||||
s.widget(_confirm_or_cancel, [&] (auto &s) {
|
||||
s.template sub_scope<Dialog::Label>(confirmed ? " Cancel "
|
||||
: " Confirm "); });
|
||||
s.template sub_scope<Label>(confirmed ? " Cancel "
|
||||
: " Confirm "); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at)
|
||||
@ -350,27 +488,84 @@ struct Dialog::Doublechecked_action_button
|
||||
_confirm_or_cancel.propagate(at);
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void clack(Clacked_at const &at, FN const &activate_fn)
|
||||
void clack(Clacked_at const &at, auto const &activate_fn)
|
||||
{
|
||||
_confirm_or_cancel.propagate(at, activate_fn);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* local includes */
|
||||
#include <view/deprecated_dialog.h>
|
||||
template <typename ENUM>
|
||||
struct Dialog::Choice : Widget<Hbox>
|
||||
{
|
||||
ENUM const _unfold_value;
|
||||
|
||||
namespace Dialog {
|
||||
Choice(ENUM const unfold_value) : _unfold_value(unfold_value) { }
|
||||
|
||||
template <typename FN>
|
||||
static inline void with_dummy_scope(Xml_generator &xml, FN const &fn)
|
||||
struct Attr
|
||||
{
|
||||
static Xml_node const hover("<hover/>");
|
||||
At const no_hover(Dialog::Event::Seq_number { }, hover);
|
||||
Scope<> scope(xml, no_hover, Dialog::Event::Dragged { }, Id { });
|
||||
fn(scope);
|
||||
unsigned left_ex, right_ex;
|
||||
ENUM unfolded;
|
||||
Id selected_item;
|
||||
};
|
||||
|
||||
struct Sub_scope
|
||||
{
|
||||
Scope<> &_scope;
|
||||
|
||||
bool const _unfolded;
|
||||
Id const _selected_item;
|
||||
|
||||
template <typename HOSTED, typename... ARGS>
|
||||
void widget(HOSTED const &hosted, ARGS &&... args)
|
||||
{
|
||||
if (_unfolded || (hosted.id == _selected_item))
|
||||
hosted._view_hosted(_scope, args...);
|
||||
}
|
||||
};
|
||||
|
||||
void view(Scope<Hbox> &s, Attr attr, auto const &fn) const
|
||||
{
|
||||
Id::Value const text = s.id.value;
|
||||
bool const unfolded = (attr.unfolded == _unfold_value);
|
||||
|
||||
s.sub_scope<Vbox>([&] (Scope<Hbox, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(attr.left_ex);
|
||||
s.sub_scope<Float>([&] (Scope<Hbox, Vbox, Float> &s) {
|
||||
s.attribute("north", true);
|
||||
s.attribute("west", true);
|
||||
s.sub_scope<Frame>([&] (Scope<Hbox, Vbox, Float, Frame> &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_scope<Hbox>([&] (Scope<Hbox, Vbox, Float, Frame, Hbox> &s) {
|
||||
s.sub_scope<Label>(text);
|
||||
s.sub_scope<Button_vgap>(); }); }); }); });
|
||||
|
||||
s.sub_scope<Frame>([&] (Scope<Hbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Hbox, Frame, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(attr.right_ex);
|
||||
s.as_new_scope([&] (Scope<> &s) {
|
||||
Sub_scope sub_scope { s, unfolded, attr.selected_item };
|
||||
fn(sub_scope); }); }); });
|
||||
}
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, ENUM &unfolded,
|
||||
auto const &fold_all_fn, auto const &item_fn) const
|
||||
{
|
||||
if (unfolded != _unfold_value) {
|
||||
unfolded = _unfold_value;
|
||||
return;
|
||||
}
|
||||
|
||||
bool clicked_at_item = false;
|
||||
Hbox::with_narrowed_at(at, [&] (Clicked_at const &at) {
|
||||
Frame::with_narrowed_at(at, [&] (Clicked_at const &at) {
|
||||
Vbox::with_narrowed_at(at, [&] (Clicked_at const &at) {
|
||||
clicked_at_item = true;
|
||||
item_fn(at); }); }); });
|
||||
|
||||
if (!clicked_at_item)
|
||||
fold_all_fn();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DIALOG_H_ */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Generate download-status view
|
||||
* \brief Generate download-status widget
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-18
|
||||
*/
|
||||
@ -11,17 +11,17 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__DOWNLOAD_STATUS_DIALOG_H_
|
||||
#define _VIEW__DOWNLOAD_STATUS_DIALOG_H_
|
||||
#ifndef _VIEW__DOWNLOAD_STATUS_WIDGET_H_
|
||||
#define _VIEW__DOWNLOAD_STATUS_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <model/download_queue.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Download_status_dialog; }
|
||||
namespace Sculpt { struct Download_status_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Download_status_dialog : Titled_frame
|
||||
struct Sculpt::Download_status_widget : Titled_frame
|
||||
{
|
||||
void view(Scope<Frame> &s, Xml_node const &state, Download_queue const &download_queue) const
|
||||
{
|
||||
@ -62,4 +62,4 @@ struct Sculpt::Download_status_dialog : Titled_frame
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__DOWNLOAD_STATUS_DIALOG_H_ */
|
||||
#endif /* _VIEW__DOWNLOAD_STATUS_WIDGET_H_ */
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
* Copyright (C) 2020-2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
@ -21,49 +21,35 @@
|
||||
namespace Sculpt { struct File_browser_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::File_browser_dialog : Noncopyable, Deprecated_dialog
|
||||
struct Sculpt::File_browser_dialog : Top_level_dialog
|
||||
{
|
||||
Runtime_config const &_runtime_config;
|
||||
File_browser_state const &_state;
|
||||
|
||||
Hoverable_item _fs_button { };
|
||||
Hoverable_item _entry { };
|
||||
Hoverable_item _path_elem { };
|
||||
Hoverable_item _action_button { };
|
||||
|
||||
void reset() override { }
|
||||
|
||||
using Fs_name = File_browser_state::Fs_name;
|
||||
using Index = File_browser_state::Index;
|
||||
using Sub_dir = File_browser_state::Path;
|
||||
using Path = File_browser_state::Path;
|
||||
using File = File_browser_state::Path;
|
||||
using Name = String<128>;
|
||||
|
||||
struct Action : Interface, Noncopyable
|
||||
{
|
||||
virtual void browse_file_system(Fs_name const &) = 0;
|
||||
|
||||
virtual void browse_sub_directory(Sub_dir const &) = 0;
|
||||
|
||||
virtual void browse_abs_directory(Path const &) = 0;
|
||||
|
||||
virtual void browse_parent_directory() = 0;
|
||||
|
||||
virtual void view_file(File const &) = 0;
|
||||
|
||||
virtual void edit_file(File const &) = 0;
|
||||
|
||||
virtual void revert_edited_file() = 0;
|
||||
|
||||
virtual void save_edited_file() = 0;
|
||||
};
|
||||
|
||||
using Name = String<128>;
|
||||
Action &_action;
|
||||
|
||||
template <typename FN>
|
||||
void _for_each_path_elem(FN const &fn) const
|
||||
static void _for_each_path_elem(Path const path, FN const &fn)
|
||||
{
|
||||
char const *curr = _state.path.string();
|
||||
char const *curr = path.string();
|
||||
|
||||
fn(Path("/"));
|
||||
|
||||
@ -84,213 +70,246 @@ struct Sculpt::File_browser_dialog : Noncopyable, Deprecated_dialog
|
||||
}
|
||||
}
|
||||
|
||||
Path _path_elem_at_index(unsigned index) const
|
||||
static Path _path_elem_at_index(Path const path, unsigned index)
|
||||
{
|
||||
Path result { };
|
||||
unsigned i = 0;
|
||||
|
||||
_for_each_path_elem([&] (Path const &elem) {
|
||||
_for_each_path_elem(path, [&] (Path const &elem) {
|
||||
if (i++ == index)
|
||||
result = elem; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _gen_path_elements(Xml_generator &xml) const
|
||||
static void _with_matching_entry(At const &at, auto const fn)
|
||||
{
|
||||
gen_named_node(xml, "hbox", "back", [&] () {
|
||||
Id const entry_id = at.matching_id<Vbox, Frame, Vbox, Entry>();
|
||||
unsigned index = ~0U;
|
||||
if (ascii_to(entry_id.value.string(), index)) {
|
||||
Hosted_entry entry { entry_id, index };
|
||||
fn(entry);
|
||||
}
|
||||
}
|
||||
|
||||
struct Navigation_entry : Widget<Left_floating_hbox>
|
||||
{
|
||||
struct Back : Widget<Float>
|
||||
{
|
||||
void view(Scope<Float> &s) const
|
||||
{
|
||||
s.sub_scope<Button>([&] (Scope<Float, Button> &s) {
|
||||
if (s.hovered()) s.attribute("hovered", "yes");
|
||||
s.attribute("style", "back");
|
||||
s.sub_scope<Hbox>();
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &, auto const &fn) { fn(); }
|
||||
};
|
||||
|
||||
Hosted<Left_floating_hbox, Back> _back { Id { "back" } };
|
||||
|
||||
void view(Scope<Left_floating_hbox> &s, Path const &path, bool allow_back) const
|
||||
{
|
||||
if (allow_back)
|
||||
s.widget(_back);
|
||||
|
||||
/* don't hover last path element */
|
||||
unsigned count = 0;
|
||||
_for_each_path_elem(path, [&] (Path const &) { count++; });
|
||||
|
||||
unsigned i = 0;
|
||||
_for_each_path_elem([&] (Path const elem) {
|
||||
_for_each_path_elem(path, [&] (Path const elem) {
|
||||
Id const id { { i++ } };
|
||||
bool const last = (i == count);
|
||||
s.sub_scope<Button>(id, [&] (Scope<Left_floating_hbox, Button> &s) {
|
||||
|
||||
gen_named_node(xml, "button", Index(i), [&] () {
|
||||
if (!_state.modified)
|
||||
_path_elem.gen_hovered_attr(xml, Index(i));
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", elem);
|
||||
});
|
||||
if (allow_back && s.hovered() && !last)
|
||||
s.attribute("hovered", "yes");
|
||||
|
||||
if (last)
|
||||
s.attribute("style", "unimportant");
|
||||
|
||||
s.sub_scope<Label>(elem);
|
||||
});
|
||||
i++;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _gen_back_entry(Xml_generator &xml) const
|
||||
void click(Clicked_at const &at, Path const &path, Action &action)
|
||||
{
|
||||
Id const elem_id = at.matching_id<Left_floating_hbox, Button>();
|
||||
if (elem_id.valid()) {
|
||||
Genode::Path<256> abs_path { "/" };
|
||||
for (unsigned i = 0; i < 20; i++) {
|
||||
abs_path.append_element(_path_elem_at_index(path, i).string());
|
||||
if (Id { { i } } == elem_id)
|
||||
break;
|
||||
}
|
||||
action.browse_abs_directory(Path(abs_path));
|
||||
}
|
||||
|
||||
_back.propagate(at, [&] { action.browse_parent_directory(); });
|
||||
}
|
||||
};
|
||||
|
||||
struct Conditional_button : Widget<Button>
|
||||
{
|
||||
gen_named_node(xml, "hbox", "back", [&] () {
|
||||
void view(Scope<Button> &s, bool condition, bool selected) const
|
||||
{
|
||||
if (s.hovered()) s.attribute("hovered", "yes");
|
||||
if (selected) s.attribute("selected", "yes");
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
if (!condition)
|
||||
s.attribute("style", "invisible");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
if (!_state.modified) {
|
||||
gen_named_node(xml, "float", "button", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
xml.attribute("style", "back");
|
||||
_entry.gen_hovered_attr(xml, "back");
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
});
|
||||
}
|
||||
s.sub_scope<Label>(s.id.value, [&] (auto &s) {
|
||||
if (!condition) s.attribute("style", "invisible"); });
|
||||
}
|
||||
|
||||
_gen_path_elements(xml);
|
||||
});
|
||||
});
|
||||
void click(Clicked_at const &, auto const &fn) { fn(); }
|
||||
};
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_entry(Xml_generator &xml, Xml_node node, Index const &index,
|
||||
char const *style) const
|
||||
/*
|
||||
* Use button instances accross entries to keep their internal state
|
||||
* independent from the lifetime of 'Entry' objects, which exist only
|
||||
* temporarily.
|
||||
*/
|
||||
struct Entry_buttons
|
||||
{
|
||||
Name const name = node.attribute_value("name", Name());
|
||||
/* scope hierarchy relative to 'Entry' */
|
||||
Hosted<Hbox, Float, Hbox, Conditional_button>
|
||||
edit { Id { "Edit" } },
|
||||
view { Id { "View" } };
|
||||
|
||||
bool const entry_edited = (name == _state.edited_file);
|
||||
Hosted<Hbox, Float, Hbox, Deferred_action_button>
|
||||
revert { Id { "Revert" } },
|
||||
save { Id { "Save" } };
|
||||
|
||||
/* while editing one file, hide all others */
|
||||
if (!entry_edited && _state.modified)
|
||||
return;
|
||||
} _entry_buttons { };
|
||||
|
||||
gen_named_node(xml, "hbox", index, [&] () {
|
||||
struct Entry : Widget<Hbox>
|
||||
{
|
||||
unsigned const index;
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
Entry(unsigned index) : index(index) { }
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
void view(Scope<Hbox> &s, File_browser_state const &state,
|
||||
Xml_node const &node, auto const &style,
|
||||
Entry_buttons const &buttons) const
|
||||
{
|
||||
Name const name = node.attribute_value("name", Name());
|
||||
bool const hovered = (s.hovered() && !state.modified);
|
||||
bool const selected = (name == state.edited_file);
|
||||
|
||||
bool const selected = (name == _state.edited_file);
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
/* while editing one file, hide all others */
|
||||
if (!selected && state.modified)
|
||||
return;
|
||||
|
||||
xml.attribute("style", style);
|
||||
s.sub_scope<Float>([&] (Scope<Hbox, Float> &s) {
|
||||
s.attribute("west", "yes");
|
||||
s.sub_scope<Hbox>([&] (Scope<Hbox, Float, Hbox> &s) {
|
||||
s.sub_scope<Icon>(style, Icon::Attr { .hovered = hovered,
|
||||
.selected = selected });
|
||||
s.sub_scope<Label>(Path(" ", name)); }); });
|
||||
|
||||
if (!_state.modified)
|
||||
_entry.gen_hovered_attr(xml, index);
|
||||
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", name)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "middle", [&] () { });
|
||||
|
||||
gen_named_node(xml, "float", "right", [&] () {
|
||||
xml.attribute("east", "yes");
|
||||
s.sub_scope<Float>([&] (Scope<Hbox, Float> &s) {
|
||||
s.attribute("east", "yes");
|
||||
|
||||
/* show no operation buttons for directories */
|
||||
if (node.type() == "dir")
|
||||
return;
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () {
|
||||
s.sub_scope<Hbox>([&] (Scope<Hbox, Float, Hbox> &s) {
|
||||
|
||||
bool const entry_hovered = _entry.hovered(index);
|
||||
bool const interesting = entry_hovered || entry_edited;
|
||||
bool const writeable = node.attribute_value("writeable", false);
|
||||
bool const interesting = hovered || selected;
|
||||
bool const writeable = node.attribute_value("writeable", false);
|
||||
|
||||
if (writeable) {
|
||||
if (!state.modified)
|
||||
s.widget(buttons.edit, interesting, selected);
|
||||
|
||||
if (!_state.modified) {
|
||||
gen_named_node(xml, "button", "edit", [&] () {
|
||||
if (interesting) {
|
||||
_action_button.gen_hovered_attr(xml, "edit");
|
||||
if (entry_edited)
|
||||
xml.attribute("selected", "yes");
|
||||
} else {
|
||||
xml.attribute("style", "invisible");
|
||||
}
|
||||
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", "Edit");
|
||||
if (!interesting)
|
||||
xml.attribute("style", "invisible");
|
||||
});
|
||||
});
|
||||
if (selected && state.modified) {
|
||||
s.widget(buttons.revert);
|
||||
s.widget(buttons.save);
|
||||
}
|
||||
|
||||
if (entry_edited && _state.modified) {
|
||||
|
||||
auto gen_action_button = [&] (auto id, auto text)
|
||||
{
|
||||
gen_named_node(xml, "button", id, [&] () {
|
||||
_action_button.gen_hovered_attr(xml, id);
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", text); }); });
|
||||
};
|
||||
|
||||
gen_action_button("revert", "Revert");
|
||||
gen_action_button("save", "Save");
|
||||
}
|
||||
|
||||
} else {
|
||||
gen_named_node(xml, "button", "view", [&] () {
|
||||
|
||||
if (interesting) {
|
||||
_action_button.gen_hovered_attr(xml, "view");
|
||||
if (entry_edited)
|
||||
xml.attribute("selected", "yes");
|
||||
} else {
|
||||
xml.attribute("style", "invisible");
|
||||
}
|
||||
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", "View");
|
||||
if (!interesting)
|
||||
xml.attribute("style", "invisible");
|
||||
});
|
||||
});
|
||||
s.widget(buttons.view, interesting, selected);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _gen_file_system(Xml_generator &xml, Service const &service) const
|
||||
void click(Clicked_at const &at, Xml_node const &node,
|
||||
Entry_buttons &buttons, Action &action)
|
||||
{
|
||||
Name const name = node.attribute_value("name", Name());
|
||||
|
||||
if (node.has_type("dir")) {
|
||||
action.browse_sub_directory(name);
|
||||
return;
|
||||
}
|
||||
|
||||
buttons.edit .propagate(at, [&] { action.edit_file(name); });
|
||||
buttons.view .propagate(at, [&] { action.view_file(name); });
|
||||
buttons.revert.propagate(at);
|
||||
buttons.save .propagate(at);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, Entry_buttons &buttons, Action &action)
|
||||
{
|
||||
buttons.revert.propagate(at, [&] { action.revert_edited_file(); });
|
||||
buttons.save .propagate(at, [&] { action.save_edited_file(); });
|
||||
}
|
||||
};
|
||||
|
||||
Hosted<Vbox, Frame, Vbox, Navigation_entry> _nav_entry { Id { "nav" } };
|
||||
|
||||
using Hosted_entry = Hosted<Vbox, Frame, Vbox, Entry>;
|
||||
|
||||
void _view_file_system(Scope<Vbox> &s, Service const &service) const
|
||||
{
|
||||
bool const parent = !service.server.valid();
|
||||
Label const name = parent ? Start_name(service.label) : service.server;
|
||||
|
||||
bool const parent = !service.server.valid();
|
||||
Start_name const name = parent ? Start_name(service.label) : service.server;
|
||||
Start_name const pretty_name { Pretty(name) };
|
||||
|
||||
bool const selected = (_state.browsed_fs == name);
|
||||
bool const selected = (_state.browsed_fs == name);
|
||||
|
||||
if (_state.text_area.constructed() && _state.modified && !selected)
|
||||
return;
|
||||
|
||||
gen_named_node(xml, "frame", name, [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("min_ex", "50"); });
|
||||
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(50);
|
||||
s.sub_scope<Button>(Id { name }, [&] (Scope<Vbox, Frame, Vbox, Button> &s) {
|
||||
|
||||
gen_named_node(xml, "button", name, [&] () {
|
||||
if (!_state.modified)
|
||||
_fs_button.gen_hovered_attr(xml, name);
|
||||
if (!_state.modified && s.hovered())
|
||||
s.attribute("hovered", "yes");
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", true);
|
||||
s.attribute("selected", true);
|
||||
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", pretty_name);
|
||||
xml.attribute("style", "title");
|
||||
});
|
||||
s.sub_scope<Label>(pretty_name, [&] (auto &s) {
|
||||
s.attribute("style", "title"); });
|
||||
});
|
||||
|
||||
if (selected) {
|
||||
unsigned cnt = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
_state.with_query_result([&] (Xml_node node) {
|
||||
node.with_optional_sub_node("dir", [&] (Xml_node listing) {
|
||||
_state.with_query_result([&] (Xml_node const &node) {
|
||||
node.with_optional_sub_node("dir", [&] (Xml_node const &listing) {
|
||||
|
||||
if (_state.path != "/")
|
||||
_gen_back_entry(xml);
|
||||
s.widget(_nav_entry, _state.path, !_state.modified);
|
||||
|
||||
listing.for_each_sub_node("dir", [&] (Xml_node dir) {
|
||||
_gen_entry(xml, dir, Index(cnt++), "enter"); });
|
||||
listing.for_each_sub_node("dir", [&] (Xml_node const &dir) {
|
||||
unsigned const index = count++;
|
||||
Hosted_entry entry { Id { { index } }, index };
|
||||
s.widget(entry, _state, dir, "enter", _entry_buttons); });
|
||||
|
||||
listing.for_each_sub_node("file", [&] (Xml_node file) {
|
||||
_gen_entry(xml, file, Index(cnt++), "radio"); });
|
||||
listing.for_each_sub_node("file", [&] (Xml_node const &file) {
|
||||
unsigned const index = count++;
|
||||
Hosted_entry entry { Id { { index } }, index };
|
||||
s.widget(entry, _state, file, "radio", _entry_buttons); });
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -298,86 +317,46 @@ struct Sculpt::File_browser_dialog : Noncopyable, Deprecated_dialog
|
||||
});
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
xml.node("vbox", [&] () {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox> &s) {
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
if (service.type == Service::Type::FILE_SYSTEM)
|
||||
_gen_file_system(xml, service); }); });
|
||||
}
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
return any_hover_changed(
|
||||
_entry.match(hover, "vbox", "frame", "vbox", "hbox", "name"),
|
||||
_path_elem.match(hover, "vbox", "frame", "vbox", "hbox", "float", "hbox", "hbox", "button", "name"),
|
||||
_fs_button.match(hover, "vbox", "frame", "vbox", "button", "name"),
|
||||
_action_button.match(hover, "vbox", "frame", "vbox", "hbox", "float", "hbox", "button", "name"));
|
||||
}
|
||||
|
||||
Click_result click(Action &action)
|
||||
{
|
||||
Click_result result = Click_result::IGNORED;
|
||||
|
||||
Fs_name const fs_name = _fs_button._hovered;
|
||||
if (fs_name.length() > 1) {
|
||||
if (!_state.modified)
|
||||
action.browse_file_system(fs_name);
|
||||
return Click_result::CONSUMED;
|
||||
}
|
||||
|
||||
Index const path_elem_idx = _path_elem._hovered;
|
||||
if (path_elem_idx.length() > 1) {
|
||||
|
||||
Genode::Path<256> abs_path { "/" };
|
||||
for (unsigned i = 0; i < 20; i++) {
|
||||
abs_path.append_element(_path_elem_at_index(i).string());
|
||||
if (Index(i) == path_elem_idx)
|
||||
break;
|
||||
}
|
||||
if (!_state.modified)
|
||||
action.browse_abs_directory(Path(abs_path));
|
||||
|
||||
return Click_result::CONSUMED;
|
||||
}
|
||||
|
||||
Index const index = _entry._hovered;
|
||||
if (index == "back") {
|
||||
if (!_state.modified)
|
||||
action.browse_parent_directory();
|
||||
}
|
||||
else if (index.length() > 1) {
|
||||
_state.with_entry_at_index(index, [&] (Xml_node entry) {
|
||||
|
||||
if (entry.has_type("dir"))
|
||||
action.browse_sub_directory(entry.attribute_value("name", Sub_dir()));
|
||||
|
||||
if (entry.has_type("file")) {
|
||||
if (_action_button.hovered("edit"))
|
||||
action.edit_file(entry.attribute_value("name", Name()));
|
||||
|
||||
if (_action_button.hovered("view"))
|
||||
action.view_file(entry.attribute_value("name", Name()));
|
||||
|
||||
if (_action_button.hovered("revert"))
|
||||
action.revert_edited_file();
|
||||
|
||||
if (_action_button.hovered("save"))
|
||||
action.save_edited_file();
|
||||
}
|
||||
});
|
||||
|
||||
return Click_result::CONSUMED;
|
||||
}
|
||||
|
||||
return result;
|
||||
_view_file_system(s, service); }); });
|
||||
}
|
||||
|
||||
File_browser_dialog(Runtime_config const &runtime_config,
|
||||
File_browser_state const &state)
|
||||
File_browser_state const &state,
|
||||
Action &action)
|
||||
:
|
||||
_runtime_config(runtime_config), _state(state)
|
||||
Top_level_dialog("file_browser"),
|
||||
_runtime_config(runtime_config), _state(state), _action(action)
|
||||
{ }
|
||||
|
||||
void click(Clicked_at const &at) override
|
||||
{
|
||||
Id const fs_id = at.matching_id<Vbox, Frame, Vbox, Button>();
|
||||
if (fs_id.valid()) {
|
||||
if (!_state.modified)
|
||||
_action.browse_file_system(fs_id.value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_state.modified)
|
||||
_nav_entry.propagate(at, _state.path, _action);
|
||||
|
||||
_with_matching_entry(at, [&] (Hosted_entry &entry) {
|
||||
_state.with_entry_at_index(entry.index, [&] (Xml_node const &node) {
|
||||
entry.propagate(at, node, _entry_buttons, _action); }); });
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at) override
|
||||
{
|
||||
_with_matching_entry(at, [&] (Hosted_entry &entry) {
|
||||
entry.propagate(at, _entry_buttons, _action); });
|
||||
}
|
||||
|
||||
void drag(Dragged_at const &) override { }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__FILE_BROWSER_H_ */
|
||||
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* \brief GUI element that shows hovering feedback
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__HOVERABLE_ITEM_H_
|
||||
#define _VIEW__HOVERABLE_ITEM_H_
|
||||
|
||||
#include <feature.h>
|
||||
#include "types.h"
|
||||
#include "xml.h"
|
||||
|
||||
namespace Sculpt { struct Hoverable_item; }
|
||||
|
||||
|
||||
struct Sculpt::Hoverable_item
|
||||
{
|
||||
typedef String<64> Id;
|
||||
|
||||
Id _hovered { };
|
||||
|
||||
enum class Hover_result { CHANGED, UNMODIFIED };
|
||||
|
||||
/**
|
||||
* Set ID to matching hover sub node name
|
||||
*
|
||||
* \return true if hovering changed
|
||||
*/
|
||||
template <typename... ARGS>
|
||||
Hover_result match(Xml_node hover, ARGS &&... args)
|
||||
{
|
||||
Id const orig = _hovered;
|
||||
_hovered = query_attribute<Id>(hover, args...);
|
||||
return (_hovered != orig) ? Hover_result::CHANGED : Hover_result::UNMODIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if item is currently hovered
|
||||
*/
|
||||
bool hovered(Id const &id) const { return id == _hovered; }
|
||||
|
||||
/**
|
||||
* Generate hovered attribute depending on the item state
|
||||
*/
|
||||
void gen_hovered_attr(Xml_generator &xml, Id const &id) const
|
||||
{
|
||||
if (Feature::VISUAL_HOVER && hovered(id))
|
||||
xml.attribute("hovered", "yes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate button attributes depending on the item state
|
||||
*/
|
||||
void gen_button_attr(Xml_generator &xml, Id const &id) const
|
||||
{
|
||||
xml.attribute("name", id);
|
||||
gen_hovered_attr(xml, id);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__HOVERABLE_ITEM_H_ */
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* \brief GUI layout helper
|
||||
* \author Norman Feske
|
||||
* \date 2022-05-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__LAYOUT_HELPER_H_
|
||||
#define _VIEW__LAYOUT_HELPER_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
|
||||
|
||||
/**
|
||||
* Inflate vertical spacing using an invisble button
|
||||
*/
|
||||
template <typename ID>
|
||||
static void gen_item_vspace(Genode::Xml_generator &xml, ID const &id)
|
||||
{
|
||||
using namespace Sculpt;
|
||||
|
||||
gen_named_node(xml, "button", id, [&] () {
|
||||
xml.attribute("style", "invisible");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", " ");
|
||||
xml.attribute("font", "title/regular");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#endif /* _VIEW__LAYOUT_HELPER_H_ */
|
@ -1,317 +0,0 @@
|
||||
/*
|
||||
* \brief Network management dialog
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
|
||||
/* local includes */
|
||||
#include "network_dialog.h"
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
void Network_dialog::_gen_access_point(Xml_generator &xml,
|
||||
Access_point const &ap) const
|
||||
{
|
||||
gen_named_node(xml, "hbox", ap.bssid, [&] {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] {
|
||||
gen_named_node(xml, "float", "left", [&] {
|
||||
gen_named_node(xml, "button", "button", [&] {
|
||||
xml.attribute("style", "radio");
|
||||
|
||||
if (_wifi_connection.connected())
|
||||
xml.attribute("selected", "yes");
|
||||
else
|
||||
_ap_item.gen_button_attr(xml, ap.bssid);
|
||||
|
||||
xml.node("hbox", [&] { });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "label", "ssid", [&] {
|
||||
xml.attribute("text", String<20>(" ", ap.ssid)); });
|
||||
|
||||
gen_named_node(xml, "label", "protection", [&] {
|
||||
xml.attribute("font", "annotation/regular");
|
||||
if (ap.protection == Access_point::WPA_PSK)
|
||||
xml.attribute("text", " (WPA) ");
|
||||
else
|
||||
xml.attribute("text", " ");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "float", "right", [&] {
|
||||
xml.attribute("east", "yes");
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("text", String<8>(ap.quality, "%")); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool Network_dialog::_selected_ap_visible() const
|
||||
{
|
||||
unsigned cnt = 0;
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return (cnt++ < _max_visible_aps) && _ap_item.selected(ap.bssid); });
|
||||
}
|
||||
|
||||
|
||||
bool Network_dialog::_selected_ap_unprotected() const
|
||||
{
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return _ap_item.selected(ap.bssid) && ap.unprotected(); });
|
||||
}
|
||||
|
||||
|
||||
bool Network_dialog::need_keyboard_focus_for_passphrase() const
|
||||
{
|
||||
if ( _wifi_connection.state == Wifi_connection::CONNECTED
|
||||
|| _wifi_connection.state == Wifi_connection::CONNECTING)
|
||||
return false;
|
||||
|
||||
if (!_nic_target.wifi())
|
||||
return false;
|
||||
|
||||
return _for_each_ap([&] (Access_point const &ap) {
|
||||
return _ap_item.selected(ap.bssid) && ap.wpa_protected(); });
|
||||
}
|
||||
|
||||
|
||||
void Network_dialog::_gen_access_point_list(Xml_generator &xml,
|
||||
bool auth_failure) const
|
||||
{
|
||||
if (_wlan_config_policy == WLAN_CONFIG_MANUAL)
|
||||
return;
|
||||
|
||||
bool const selected_ap_visible = _selected_ap_visible();
|
||||
|
||||
unsigned cnt = 0;
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
|
||||
if (cnt++ >= _max_visible_aps)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Whenever the user has selected an access point, hide all others.
|
||||
* Should the selected AP disappear from the list, show all others.
|
||||
*/
|
||||
bool const selected = _ap_item.selected(ap.bssid);
|
||||
if (selected_ap_visible && !selected)
|
||||
return;
|
||||
|
||||
_gen_access_point(xml, ap);
|
||||
|
||||
if (!selected)
|
||||
return;
|
||||
|
||||
bool const connected_to_selected_ap =
|
||||
(selected && _wifi_connection.bssid == ap.bssid)
|
||||
&& _wifi_connection.state == Wifi_connection::CONNECTED;
|
||||
|
||||
if (connected_to_selected_ap)
|
||||
return;
|
||||
|
||||
if (ap.protection == Access_point::WPA_PSK) {
|
||||
gen_named_node(xml, "label", "passphrase msg", [&] () {
|
||||
xml.attribute("text", auth_failure ? "Enter passphrase (auth failure):"
|
||||
: "Enter passphrase:");
|
||||
});
|
||||
|
||||
gen_named_node(xml, "frame", "passphrase", [&] () {
|
||||
xml.node("float", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("font", "title/regular");
|
||||
String<3*64> const passphrase(" ", _wpa_passphrase);
|
||||
xml.attribute("text", passphrase);
|
||||
xml.node("cursor", [&] () {
|
||||
xml.attribute("at", passphrase.length() - 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (_wpa_passphrase.suitable_for_connect()) {
|
||||
xml.node("button", [&] () {
|
||||
|
||||
if (_wifi_connection.state == Wifi_connection::CONNECTING)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
/* suppress hover while connecting */
|
||||
else
|
||||
_connect_item.gen_button_attr(xml, "connect");
|
||||
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "Connect"); });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Present motivational message until we get the first 'accesspoints'
|
||||
* report.
|
||||
*/
|
||||
if (cnt == 0)
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "Scanning..."); });
|
||||
}
|
||||
|
||||
|
||||
void Network_dialog::_gen_connected_ap(Xml_generator &xml, bool connected) const
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
/*
|
||||
* Try to present complete info including the quality from access-point
|
||||
* list.
|
||||
*/
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
if (!done && _wifi_connection.bssid == ap.bssid) {
|
||||
_gen_access_point(xml, ap);
|
||||
done = true;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* If access point is not present in the list, fall back to the information
|
||||
* given in the 'state' report.
|
||||
*/
|
||||
if (!done)
|
||||
_gen_access_point(xml, Access_point { _wifi_connection.bssid,
|
||||
_wifi_connection.ssid,
|
||||
Access_point::UNKNOWN });
|
||||
|
||||
gen_named_node(xml, "label", "associated", [&] () {
|
||||
xml.attribute("text", connected ? "associated"
|
||||
: "connecting");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Network_dialog::generate(Xml_generator &xml) const
|
||||
{
|
||||
gen_named_node(xml, "frame", "network", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
gen_named_node(xml, "hbox", "type", [&] () {
|
||||
|
||||
auto gen_nic_button = [&] (Hoverable_item::Id const &id,
|
||||
Nic_target::Type const type,
|
||||
String<20> const &label) {
|
||||
gen_named_node(xml, "button", id, [&] () {
|
||||
|
||||
_nic_item.gen_button_attr(xml, id);
|
||||
|
||||
if (_nic_target.type() == type)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.node("label", [&] () { xml.attribute("text", label); });
|
||||
});
|
||||
};
|
||||
|
||||
gen_nic_button("off", Nic_target::OFF, "Off");
|
||||
|
||||
/*
|
||||
* Allow interactive selection only if NIC-router configuration
|
||||
* is not manually maintained.
|
||||
*/
|
||||
if (_nic_target.managed() || _nic_target.manual_type == Nic_target::DISCONNECTED)
|
||||
gen_nic_button("disconnected", Nic_target::DISCONNECTED, "Disconnected");
|
||||
|
||||
if (_nic_target.managed() || _nic_target.manual_type == Nic_target::WIRED)
|
||||
if (_pci_info.lan_present)
|
||||
gen_nic_button("wired", Nic_target::WIRED, "Wired");
|
||||
|
||||
if (_nic_target.managed() || _nic_target.manual_type == Nic_target::WIFI)
|
||||
if (_pci_info.wifi_present)
|
||||
gen_nic_button("wifi", Nic_target::WIFI, "Wifi");
|
||||
|
||||
if (_nic_target.managed() || _nic_target.manual_type == Nic_target::MODEM)
|
||||
if (_pci_info.modem_present)
|
||||
gen_nic_button("modem", Nic_target::MODEM, "Mobile data");
|
||||
});
|
||||
|
||||
if (_nic_target.wifi() || _nic_target.wired() || _nic_target.modem()) {
|
||||
gen_named_node(xml, "frame", "nic_info", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
/*
|
||||
* If connected via Wifi, show the information of the
|
||||
* connected access point. If not connected, present
|
||||
* the complete list of access points with the option
|
||||
* to select one.
|
||||
*/
|
||||
if (_nic_target.wifi()) {
|
||||
if (_wifi_connection.connected())
|
||||
_gen_connected_ap(xml, true);
|
||||
else if (_wifi_connection.connecting())
|
||||
_gen_connected_ap(xml, false);
|
||||
else
|
||||
_gen_access_point_list(xml,
|
||||
_wifi_connection.auth_failure());
|
||||
}
|
||||
|
||||
/* append display of uplink IP address */
|
||||
if (_nic_state.ready())
|
||||
gen_named_node(xml, "label", "ip", [&] () {
|
||||
xml.attribute("text", _nic_state.ipv4); });
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Deprecated_dialog::Hover_result Network_dialog::hover(Xml_node hover)
|
||||
{
|
||||
Deprecated_dialog::Hover_result const hover_result = Deprecated_dialog::any_hover_changed(
|
||||
_nic_item .match(hover, "frame", "vbox", "hbox", "button", "name"),
|
||||
_ap_item .match(hover, "frame", "vbox", "frame", "vbox", "hbox", "name"),
|
||||
_connect_item.match(hover, "frame", "vbox", "frame", "vbox", "button", "name"));
|
||||
|
||||
_nic_info.match(hover, "vbox", "frame", "name");
|
||||
|
||||
return hover_result;
|
||||
}
|
||||
|
||||
|
||||
void Network_dialog::click(Action &action)
|
||||
{
|
||||
if (_nic_item.hovered("off")) action.nic_target(Nic_target::OFF);
|
||||
if (_nic_item.hovered("disconnected")) action.nic_target(Nic_target::DISCONNECTED);
|
||||
if (_nic_item.hovered("wired")) action.nic_target(Nic_target::WIRED);
|
||||
if (_nic_item.hovered("wifi")) action.nic_target(Nic_target::WIFI);
|
||||
if (_nic_item.hovered("modem")) action.nic_target(Nic_target::MODEM);
|
||||
|
||||
if (_wifi_connection.connected() && _ap_item.hovered(_wifi_connection.bssid)) {
|
||||
action.wifi_disconnect();
|
||||
_ap_item.reset();
|
||||
} else {
|
||||
_ap_item.toggle_selection_on_click();
|
||||
|
||||
/* immediately connect to unprotected access point when selected */
|
||||
if (_ap_item.any_selected() && _selected_ap_unprotected())
|
||||
action.wifi_connect(selected_ap());
|
||||
}
|
||||
|
||||
if (_connect_item.hovered("connect"))
|
||||
action.wifi_connect(selected_ap());
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* \brief Network management dialog
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__NETWORK_DIALOG_H_
|
||||
#define _VIEW__NETWORK_DIALOG_H_
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <model/nic_target.h>
|
||||
#include <model/nic_state.h>
|
||||
#include <model/wifi_connection.h>
|
||||
#include <model/wpa_passphrase.h>
|
||||
#include <model/pci_info.h>
|
||||
#include <view/selectable_item.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Network_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Network_dialog : Deprecated_dialog
|
||||
{
|
||||
enum Wlan_config_policy { WLAN_CONFIG_MANAGED, WLAN_CONFIG_MANUAL };
|
||||
|
||||
Nic_target const &_nic_target;
|
||||
Access_points const &_access_points;
|
||||
Wifi_connection const &_wifi_connection;
|
||||
Nic_state const &_nic_state;
|
||||
Blind_wpa_passphrase const &_wpa_passphrase;
|
||||
Wlan_config_policy const &_wlan_config_policy;
|
||||
Pci_info const &_pci_info;
|
||||
|
||||
Hoverable_item _nic_item { };
|
||||
Selectable_item _ap_item { };
|
||||
Hoverable_item _nic_info { };
|
||||
Hoverable_item _connect_item { }; /* confirm WPA passphrase */
|
||||
|
||||
bool ap_list_hovered() const { return _nic_target.wifi()
|
||||
&& _nic_info.hovered("nic_info"); }
|
||||
|
||||
/*
|
||||
* \return true if at least one access point fulfils the condition 'COND_FN'
|
||||
*/
|
||||
template <typename COND_FN>
|
||||
bool _for_each_ap(COND_FN const &cond_fn) const
|
||||
{
|
||||
bool result = false;
|
||||
_access_points.for_each([&] (Access_point const &ap) {
|
||||
result |= cond_fn(ap); });
|
||||
return result;
|
||||
}
|
||||
|
||||
Access_point::Bssid selected_ap() const { return _ap_item._selected; }
|
||||
|
||||
/* limit view to highest-quality access points */
|
||||
unsigned const _max_visible_aps = 20;
|
||||
|
||||
/* determine whether the selected AP is present in access-point list */
|
||||
bool _selected_ap_visible() const;
|
||||
|
||||
bool _selected_ap_unprotected() const;
|
||||
|
||||
void _gen_access_point(Xml_generator &, Access_point const &) const;
|
||||
void _gen_connected_ap(Xml_generator &, bool) const;
|
||||
void _gen_access_point_list(Xml_generator &, bool) const;
|
||||
|
||||
Hover_result hover(Xml_node) override;
|
||||
|
||||
void generate(Xml_generator &) const override;
|
||||
|
||||
void reset() override { }
|
||||
|
||||
bool need_keyboard_focus_for_passphrase() const;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void nic_target(Nic_target::Type) = 0;
|
||||
|
||||
virtual void wifi_connect(Access_point::Ssid) = 0;
|
||||
|
||||
virtual void wifi_disconnect() = 0;
|
||||
};
|
||||
|
||||
void click(Action &action);
|
||||
|
||||
Network_dialog(Nic_target const &nic_target,
|
||||
Access_points const &access_points,
|
||||
Wifi_connection const &wifi_connection,
|
||||
Nic_state const &nic_state,
|
||||
Blind_wpa_passphrase const &wpa_passphrase,
|
||||
Wlan_config_policy const &wlan_config_policy,
|
||||
Pci_info const &pci_info)
|
||||
:
|
||||
_nic_target(nic_target), _access_points(access_points),
|
||||
_wifi_connection(wifi_connection), _nic_state(nic_state),
|
||||
_wpa_passphrase(wpa_passphrase), _wlan_config_policy(wlan_config_policy),
|
||||
_pci_info(pci_info)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__NETWORK_DIALOG_H_ */
|
148
repos/gems/src/app/sculpt_manager/view/network_widget.h
Normal file
148
repos/gems/src/app/sculpt_manager/view/network_widget.h
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* \brief Network management widget
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__NETWORK_WIDGET_H_
|
||||
#define _VIEW__NETWORK_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <model/nic_target.h>
|
||||
#include <model/nic_state.h>
|
||||
#include <model/pci_info.h>
|
||||
#include <view/ap_selector_widget.h>
|
||||
|
||||
namespace Sculpt { struct Network_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Network_widget : Widget<Frame>
|
||||
{
|
||||
using Wlan_config_policy = Ap_selector_widget::Wlan_config_policy;
|
||||
|
||||
Nic_target const &_nic_target;
|
||||
Nic_state const &_nic_state;
|
||||
Pci_info const &_pci_info;
|
||||
|
||||
struct Action : Ap_selector_widget::Action
|
||||
{
|
||||
virtual void nic_target(Nic_target::Type) = 0;
|
||||
};
|
||||
|
||||
struct Target_selector : Widget<Hbox>
|
||||
{
|
||||
using Type = Nic_target::Type;
|
||||
|
||||
Hosted<Hbox, Select_button<Type>>
|
||||
_off { Id { "Off" }, Type::OFF },
|
||||
_local { Id { "Disconnected" }, Type::DISCONNECTED },
|
||||
_wired { Id { "Wired" }, Type::WIRED },
|
||||
_wifi { Id { "Wifi" }, Type::WIFI },
|
||||
_modem { Id { "Mobile data" }, Type::MODEM };
|
||||
|
||||
void view(Scope<Hbox> &s, Nic_target const &target, Pci_info const &pci_info) const
|
||||
{
|
||||
Type const selected = target.type();
|
||||
|
||||
s.widget(_off, selected);
|
||||
|
||||
/*
|
||||
* Allow interactive selection only if NIC-router configuration
|
||||
* is not manually maintained.
|
||||
*/
|
||||
if (target.managed() || target.manual_type == Nic_target::DISCONNECTED)
|
||||
s.widget(_local, selected);
|
||||
|
||||
if (target.managed() || target.manual_type == Nic_target::WIRED)
|
||||
if (pci_info.lan_present)
|
||||
s.widget(_wired, selected);
|
||||
|
||||
if (target.managed() || target.manual_type == Nic_target::WIFI)
|
||||
if (pci_info.wifi_present)
|
||||
s.widget(_wifi, selected);
|
||||
|
||||
if (target.managed() || target.manual_type == Nic_target::MODEM)
|
||||
if (pci_info.modem_present)
|
||||
s.widget(_modem, selected);
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Action &action)
|
||||
{
|
||||
_off .propagate(at, [&] (Type t) { action.nic_target(t); });
|
||||
_local.propagate(at, [&] (Type t) { action.nic_target(t); });
|
||||
_wired.propagate(at, [&] (Type t) { action.nic_target(t); });
|
||||
_wifi .propagate(at, [&] (Type t) { action.nic_target(t); });
|
||||
_modem.propagate(at, [&] (Type t) { action.nic_target(t); });
|
||||
}
|
||||
};
|
||||
|
||||
Hosted<Frame, Vbox, Target_selector> _target_selector { Id { "target" } };
|
||||
|
||||
Hosted<Frame, Vbox, Frame, Vbox, Ap_selector_widget> _ap_selector;
|
||||
|
||||
void _gen_connected_ap(Xml_generator &, bool) const;
|
||||
|
||||
Network_widget(Nic_target const &nic_target,
|
||||
Access_points const &access_points,
|
||||
Wifi_connection const &wifi_connection,
|
||||
Nic_state const &nic_state,
|
||||
Blind_wpa_passphrase const &wpa_passphrase,
|
||||
Wlan_config_policy const &wlan_config_policy,
|
||||
Pci_info const &pci_info)
|
||||
:
|
||||
_nic_target(nic_target), _nic_state(nic_state), _pci_info(pci_info),
|
||||
_ap_selector(Id { "aps" },
|
||||
access_points, wifi_connection,
|
||||
wlan_config_policy, wpa_passphrase)
|
||||
{ }
|
||||
|
||||
void view(Scope<Frame> &s) const
|
||||
{
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(35);
|
||||
|
||||
s.widget(_target_selector, _nic_target, _pci_info);
|
||||
|
||||
if (_nic_target.wifi() || _nic_target.wired() || _nic_target.modem()) {
|
||||
|
||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Frame, Vbox> &s) {
|
||||
|
||||
if (_nic_target.wifi())
|
||||
s.widget(_ap_selector);
|
||||
|
||||
if (_nic_state.ready())
|
||||
s.sub_scope<Label>(_nic_state.ipv4);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Action &action)
|
||||
{
|
||||
_target_selector.propagate(at, action);
|
||||
_ap_selector.propagate(at, action);
|
||||
}
|
||||
|
||||
bool need_keyboard_focus_for_passphrase() const
|
||||
{
|
||||
return _nic_target.wifi()
|
||||
&& _ap_selector.need_keyboard_focus_for_passphrase();
|
||||
}
|
||||
|
||||
bool ap_list_hovered(Hovered_at const &at) const
|
||||
{
|
||||
return _ap_selector.if_hovered(at, [&] (Hovered_at const &) {
|
||||
return _ap_selector.ap_list_shown(); });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__NETWORK_WIDGET_H_ */
|
@ -14,13 +14,9 @@
|
||||
#ifndef _VIEW__PANEL_DIALOG_H_
|
||||
#define _VIEW__PANEL_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/reporter.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <view/dialog.h>
|
||||
#include <view/activatable_item.h>
|
||||
|
||||
namespace Sculpt { struct Panel_dialog; }
|
||||
|
||||
|
@ -25,8 +25,6 @@ void Partition_operations::view(Scope<Vbox> &s,
|
||||
Partition const &partition,
|
||||
Storage_target const &used_target) const
|
||||
{
|
||||
using Label = Dialog::Label;
|
||||
|
||||
String<16> const version(device.label, ".", partition.number);
|
||||
|
||||
bool const whole_device = !partition.number.valid();
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* \brief PD/CPU route assignment dialog
|
||||
* \author Alexander Boettcher
|
||||
* \date 2021-02-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <view/pd_route_dialog.h>
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
void Pd_route_dialog::generate(Xml_generator &xml) const
|
||||
{
|
||||
/* find out number of available PD services */
|
||||
unsigned count_pd_services = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
if (service.type == Service::Type::PD)
|
||||
count_pd_services ++;
|
||||
});
|
||||
|
||||
/* don't show the PD menu if just the system PD service is available */
|
||||
if (count_pd_services <= 1)
|
||||
return;
|
||||
|
||||
{
|
||||
Route::Id const pd_id("pd_route");
|
||||
|
||||
gen_named_node(xml, "frame", pd_id, [&] () {
|
||||
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
bool const defined = _route.selected_service.constructed();
|
||||
|
||||
if (!_menu_selected) {
|
||||
_gen_route_entry(xml, pd_id,
|
||||
defined ? Component::Info(_route.selected_service->info)
|
||||
: Component::Info(_route),
|
||||
defined);
|
||||
}
|
||||
|
||||
/*
|
||||
* List of routing options
|
||||
*/
|
||||
if (_menu_selected) {
|
||||
_gen_route_entry(xml, "back", Component::Info(_route), true, "back");
|
||||
|
||||
unsigned cnt = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
Hoverable_item::Id const id("service.", cnt++);
|
||||
|
||||
bool const service_selected =
|
||||
_route.selected_service.constructed() &&
|
||||
id == _route.selected_service_id;
|
||||
|
||||
if (service.type == _route.required)
|
||||
_gen_route_entry(xml, id, service.info, service_selected);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Pd_route_dialog::click(Component &component)
|
||||
{
|
||||
if (_route_item.hovered("pd_route")) {
|
||||
_menu_selected = true;
|
||||
}
|
||||
|
||||
if (!_menu_selected)
|
||||
return;
|
||||
|
||||
unsigned cnt = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
Hoverable_item::Id const id("service.", cnt++);
|
||||
|
||||
if (!_route_item.hovered(id))
|
||||
return;
|
||||
|
||||
if (_route.selected_service.constructed()) {
|
||||
if (component.pd_route.selected_service.constructed())
|
||||
component.pd_route.selected_service.destruct();
|
||||
|
||||
_route.selected_service.destruct();
|
||||
if (_route_item.hovered(_route.selected_service_id)) {
|
||||
_route.selected_service_id = Hoverable_item::Id();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
component.pd_route.selected_service.construct(service);
|
||||
|
||||
_route.selected_service.construct(service);
|
||||
_route.selected_service_id = id;
|
||||
|
||||
_menu_selected = false;
|
||||
});
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* \brief PD/CPU route assignment dialog
|
||||
* \author Alexander Boettcher
|
||||
* \date 2021-02-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__PD_ROUTE_DIALOG_H_
|
||||
#define _VIEW__PD_ROUTE_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/reporter.h>
|
||||
#include <depot/archive.h>
|
||||
|
||||
/* local includes */
|
||||
#include <xml.h>
|
||||
#include <model/component.h>
|
||||
#include <model/runtime_config.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Pd_route_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Pd_route_dialog : Noncopyable, Deprecated_dialog
|
||||
{
|
||||
Route _route { "<pd/>" };
|
||||
Hoverable_item _route_item { };
|
||||
bool _menu_selected { false };
|
||||
|
||||
Runtime_config const &_runtime_config;
|
||||
|
||||
Pd_route_dialog(Runtime_config const &runtime_config)
|
||||
:
|
||||
_runtime_config(runtime_config)
|
||||
{ }
|
||||
|
||||
Hover_result hover(Xml_node hover_node) override
|
||||
{
|
||||
Deprecated_dialog::Hover_result const hover_result = hover(hover_node);
|
||||
return hover_result;
|
||||
}
|
||||
|
||||
template <typename... ARGS>
|
||||
Hover_result hover(Xml_node hover, ARGS &&... args)
|
||||
{
|
||||
Deprecated_dialog::Hover_result const hover_result = Deprecated_dialog::any_hover_changed(
|
||||
_route_item.match(hover, args...));
|
||||
|
||||
return hover_result;
|
||||
}
|
||||
|
||||
void click(Component &);
|
||||
|
||||
void generate(Xml_generator &xml) const override;
|
||||
|
||||
void reset() override
|
||||
{
|
||||
if (_route.selected_service.constructed())
|
||||
_route.selected_service.destruct();
|
||||
_route_item._hovered = Hoverable_item::Id();
|
||||
_menu_selected = false;
|
||||
}
|
||||
|
||||
void click()
|
||||
{
|
||||
if (_route_item.hovered("pd_route"))
|
||||
_menu_selected = true;
|
||||
}
|
||||
|
||||
void _gen_route_entry(Xml_generator &xml,
|
||||
Start_name const &name,
|
||||
Start_name const &text,
|
||||
bool selected, char const *style = "radio") const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", style);
|
||||
_route_item.gen_hovered_attr(xml, name);
|
||||
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__PD_ROUTE_DIALOG_H_ */
|
99
repos/gems/src/app/sculpt_manager/view/pd_route_widget.h
Normal file
99
repos/gems/src/app/sculpt_manager/view/pd_route_widget.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* \brief PD/CPU route assignment widget
|
||||
* \author Norman Feske
|
||||
* \date 2023-10-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__PD_ROUTE_WIDGET_H_
|
||||
#define _VIEW__PD_ROUTE_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <model/component.h>
|
||||
#include <model/runtime_config.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Pd_route_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Pd_route_widget : Widget<Vbox>
|
||||
{
|
||||
Runtime_config const &_runtime_config;
|
||||
|
||||
Pd_route_widget(Runtime_config const &runtime_config)
|
||||
:
|
||||
_runtime_config(runtime_config)
|
||||
{ }
|
||||
|
||||
void view(Scope<Vbox> &s, Id const &selected_route, Component const &component) const
|
||||
{
|
||||
using Route_entry = Hosted<Vbox, Menu_entry>;
|
||||
using Service_entry = Hosted<Vbox, Menu_entry>;
|
||||
using Info = Component::Info;
|
||||
|
||||
Id const pd_route_id = s.id;
|
||||
|
||||
bool const selected = (selected_route == pd_route_id);
|
||||
|
||||
if (!selected) {
|
||||
bool const defined = component.pd_route.selected_service.constructed();
|
||||
|
||||
Route_entry entry { pd_route_id };
|
||||
s.widget(entry, defined,
|
||||
defined ? Info(component.pd_route.selected_service->info)
|
||||
: Info("PD"));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* List of routing options
|
||||
*/
|
||||
Route_entry back { Id { "back" } };
|
||||
s.widget(back, true, "PD", "back");
|
||||
|
||||
unsigned count = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
Id const service_id { Id::Value("service.", count++) };
|
||||
|
||||
bool const service_selected =
|
||||
component.pd_route.selected_service.constructed() &&
|
||||
service_id.value == component.pd_route.selected_service_id;
|
||||
|
||||
if (service.type == Service::Type::PD) {
|
||||
Service_entry entry { service_id };
|
||||
s.widget(entry, service_selected, service.info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Component &component)
|
||||
{
|
||||
Id const id = at.matching_id<Vbox, Menu_entry>();
|
||||
|
||||
unsigned count = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
Id const service_id { Id::Value("service.", count++) };
|
||||
|
||||
if (id == service_id) {
|
||||
Route &route = component.pd_route;
|
||||
if (route.selected_service_id == service_id.value) {
|
||||
route.selected_service.destruct();
|
||||
route.selected_service_id = { };
|
||||
} else {
|
||||
route.selected_service.construct(service);
|
||||
route.selected_service_id = service_id.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__PD_ROUTE_WIDGET_H_ */
|
@ -17,153 +17,160 @@
|
||||
using namespace Sculpt;
|
||||
|
||||
|
||||
void Popup_dialog::_gen_pkg_info(Xml_generator &xml,
|
||||
Component const &component) const
|
||||
static Dialog::Id launcher_id(unsigned n) { return { Dialog::Id::Value("launcher ", n) }; }
|
||||
static Dialog::Id user_id (unsigned n) { return { Dialog::Id::Value("user ", n) }; }
|
||||
|
||||
|
||||
static void view_component_info(auto &s, Component const &component)
|
||||
{
|
||||
if (component.info.length() > 1) {
|
||||
gen_named_node(xml, "label", "info", [&] () {
|
||||
xml.attribute("text", Component::Info(" ", component.info, " ")); });
|
||||
s.named_sub_node("label", "info", [&] {
|
||||
s.attribute("text", Component::Info(" ", component.info, " ")); });
|
||||
|
||||
_gen_info_label(xml, "pad1", "");
|
||||
s.template sub_scope<Annotation>("");
|
||||
}
|
||||
_gen_info_label(xml, "path", component.path);
|
||||
s.template sub_scope<Annotation>(component.path);
|
||||
}
|
||||
|
||||
|
||||
void Popup_dialog::_gen_pkg_elements(Xml_generator &xml,
|
||||
Component const &component) const
|
||||
void Popup_dialog::_view_pkg_elements(Scope<Frame, Vbox> &s,
|
||||
Component const &component) const
|
||||
{
|
||||
typedef Component::Info Info;
|
||||
using Info = Component::Info;
|
||||
|
||||
_gen_sub_menu_title(xml, "back", Index_menu::Name("Add ", Pretty(_construction_name)));
|
||||
s.widget(_back, Index_menu::Name("Add ", Pretty(_construction_name)));
|
||||
|
||||
_gen_pkg_info(xml, component);
|
||||
view_component_info(s, component);
|
||||
|
||||
_gen_info_label(xml, "resources", Info(Capacity{component.ram}, " ",
|
||||
component.caps, " caps"));
|
||||
_gen_info_label(xml, "pad2", "");
|
||||
s.sub_scope<Annotation>(Info(Capacity{component.ram}, " ",
|
||||
component.caps, " caps"));
|
||||
s.sub_scope<Vgap>();
|
||||
|
||||
unsigned cnt = 0;
|
||||
unsigned count = 0;
|
||||
component.routes.for_each([&] (Route const &route) {
|
||||
|
||||
Route::Id const id(cnt++);
|
||||
Id const id { Id::Value { count++ } };
|
||||
|
||||
gen_named_node(xml, "frame", id, [&] () {
|
||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Frame, Vbox> &s) {
|
||||
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
bool const selected = _route_selected(id);
|
||||
bool const selected = _route_selected(id.value);
|
||||
bool const defined = route.selected_service.constructed();
|
||||
|
||||
if (!selected) {
|
||||
_gen_route_entry(xml, id,
|
||||
defined ? Info(route.selected_service->info)
|
||||
: Info(route),
|
||||
defined);
|
||||
Route_entry entry { id };
|
||||
s.widget(entry, defined,
|
||||
defined ? Info(route.selected_service->info)
|
||||
: Info(route));
|
||||
}
|
||||
|
||||
/*
|
||||
* List of routing options
|
||||
*/
|
||||
if (selected) {
|
||||
_gen_route_entry(xml, "back", Info(route), true, "back");
|
||||
Route_entry back { Id { "back" } };
|
||||
s.widget(back, true, Info(route), "back");
|
||||
|
||||
unsigned cnt = 0;
|
||||
unsigned count = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
Hoverable_item::Id const id("service.", cnt++);
|
||||
Id const service_id { Id::Value("service.", count++) };
|
||||
|
||||
bool const service_selected =
|
||||
route.selected_service.constructed() &&
|
||||
id == route.selected_service_id;
|
||||
service_id.value == route.selected_service_id;
|
||||
|
||||
if (service.type == route.required)
|
||||
_gen_route_entry(xml, id, service.info, service_selected);
|
||||
if (service.type == route.required) {
|
||||
Service_entry entry { service_id };
|
||||
s.widget(entry, service_selected, service.info);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
_pd_route.generate(xml);
|
||||
/* don't show the PD menu if only the system PD service is available */
|
||||
if (_runtime_config.num_service_options(Service::Type::PD) > 1)
|
||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
||||
s.widget(_pd_route, _selected_route, component); });
|
||||
|
||||
if (_resources.constructed()) {
|
||||
gen_named_node(xml, "frame", "resources", [&] {
|
||||
xml.node("vbox", [&] () {
|
||||
s.sub_scope<Frame>(Id { "resources" }, [&] (Scope<Frame, Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Frame, Vbox> &s) {
|
||||
|
||||
bool const selected = _route_selected("resources");
|
||||
bool const selected = _route_selected("resources");
|
||||
|
||||
if (!selected)
|
||||
_gen_route_entry(xml, "resources",
|
||||
"Resource assignment ...", false, "enter");
|
||||
if (!selected) {
|
||||
Route_entry entry { Id { "resources" } };
|
||||
s.widget(entry, false, "Resource assignment ...", "enter");
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
_gen_route_entry(xml, "back", "Resource assignment ...",
|
||||
true, "back");
|
||||
if (selected) {
|
||||
Route_entry entry { Id { "back" } };
|
||||
s.widget(entry, true, "Resource assignment ...", "back");
|
||||
|
||||
_resources->generate(xml);
|
||||
}
|
||||
});
|
||||
s.widget(_resources, component);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
gen_named_node(xml, "frame", "debug", [&] {
|
||||
xml.node("vbox", [&] {
|
||||
_debug.generate(xml); }); });
|
||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
||||
s.widget(_debug, component); });
|
||||
|
||||
/*
|
||||
* Display "Add component" button once all routes are defined
|
||||
*/
|
||||
if (component.all_routes_defined()) {
|
||||
gen_named_node(xml, "button", "launch", [&] () {
|
||||
_action_item.gen_button_attr(xml, "launch");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "Add component"); });
|
||||
});
|
||||
}
|
||||
if (component.all_routes_defined())
|
||||
s.widget(_launch);
|
||||
}
|
||||
|
||||
|
||||
void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_users) const
|
||||
void Popup_dialog::_view_menu_elements(Scope<Frame, Vbox> &s, Xml_node const &depot_users) const
|
||||
{
|
||||
/*
|
||||
* Lauchers
|
||||
*/
|
||||
if (_state == TOP_LEVEL || _state < DEPOT_SHOWN) {
|
||||
unsigned count = 0;
|
||||
_launchers.for_each([&] (Launchers::Info const &info) {
|
||||
|
||||
/* allow each launcher to be used only once */
|
||||
if (_runtime_info.present_in_runtime(info.path))
|
||||
return;
|
||||
|
||||
_gen_menu_entry(xml, info.path, Pretty(info.path), false);
|
||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { launcher_id(count++) };
|
||||
s.widget(menu_entry, false, String<100>(Pretty(info.path)));
|
||||
});
|
||||
|
||||
_gen_menu_entry(xml, "depot", "Depot ...", false);
|
||||
Hosted<Frame, Vbox, Menu_entry> depot_menu_entry { Id { "depot" } };
|
||||
s.widget(depot_menu_entry, false, "Depot ...");
|
||||
}
|
||||
|
||||
/*
|
||||
* Depot users with an available index
|
||||
*/
|
||||
if (_state == DEPOT_SHOWN || _state == INDEX_REQUESTED) {
|
||||
_gen_sub_menu_title(xml, "back", "Depot");
|
||||
s.widget(_back, "Depot");
|
||||
|
||||
unsigned count = 0;
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
||||
|
||||
User const name = user.attribute_value("name", User());
|
||||
User const name = user.attribute_value("name", User());
|
||||
bool const selected = (_selected_user == name);
|
||||
Id const id = user_id(count++);
|
||||
|
||||
if (_index_avail(name))
|
||||
_gen_menu_entry(xml, name, User(name, " ..."), selected);
|
||||
if (_index_avail(name)) {
|
||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { id };
|
||||
s.widget(menu_entry, selected, User(name, " ..."));
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Depot selection menu item
|
||||
*/
|
||||
if (_state == DEPOT_SHOWN || _state == INDEX_REQUESTED) {
|
||||
|
||||
if (_nic_ready())
|
||||
_gen_menu_entry(xml, "selection", "Selection ...", false);
|
||||
if (_nic_ready()) {
|
||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { Id { "selection" } };
|
||||
s.widget(menu_entry, false, "Selection ...");
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,17 +178,20 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
* List of depot users for removing/adding indices
|
||||
*/
|
||||
if (_state == DEPOT_SELECTION) {
|
||||
_gen_sub_menu_title(xml, "back", "Selection");
|
||||
s.widget(_back, "Selection");
|
||||
|
||||
unsigned count = 0;
|
||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
||||
|
||||
User const name = user.attribute_value("name", User());
|
||||
bool const selected = _index_avail(name);
|
||||
Id const id = user_id(count++);
|
||||
|
||||
String<32> const suffix = _download_queue.in_progress(_index_path(name))
|
||||
? " fetch... " : " ";
|
||||
|
||||
_gen_menu_entry(xml, name, User(name, suffix), selected, "checkbox");
|
||||
Hosted<Frame, Vbox, Menu_entry> user_entry { id };
|
||||
s.widget(user_entry, selected, User(name, suffix), "checkbox");
|
||||
});
|
||||
}
|
||||
|
||||
@ -193,7 +203,7 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
if (_menu._level)
|
||||
title = Index_menu::Name(title, " ", _menu, " ");
|
||||
|
||||
_gen_sub_menu_title(xml, "back", title);
|
||||
s.widget(_back, title);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -201,14 +211,15 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
*/
|
||||
if (_state >= INDEX_SHOWN && _state < PKG_SHOWN) {
|
||||
|
||||
unsigned cnt = 0;
|
||||
unsigned count = 0;
|
||||
_for_each_menu_item([&] (Xml_node item) {
|
||||
|
||||
Hoverable_item::Id const id(cnt);
|
||||
Id const id { Id::Value(count) };
|
||||
|
||||
if (item.has_type("index")) {
|
||||
auto const name = item.attribute_value("name", Index_menu::Name());
|
||||
_gen_menu_entry(xml, id, Index_menu::Name(name, " ..."), false);
|
||||
Hosted<Frame, Vbox, Menu_entry> entry { id };
|
||||
s.widget(entry, false, Index_menu::Name(name, " ..."));
|
||||
}
|
||||
|
||||
if (item.has_type("pkg")) {
|
||||
@ -229,15 +240,15 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
String<100> const text(Pretty(name), " " "(", version, ")",
|
||||
installing ? " installing... " : "... ");
|
||||
|
||||
_gen_menu_entry(xml, id, text, selected);
|
||||
Hosted<Frame, Vbox, Menu_entry> entry { id };
|
||||
s.widget(entry, selected, text);
|
||||
|
||||
if (selected && !installing) {
|
||||
|
||||
_construction_info.with_construction([&] (Component const &component) {
|
||||
|
||||
gen_named_node(xml, "float", "install", [&] () {
|
||||
|
||||
gen_named_node(xml, "vbox", "vbox", [&] () {
|
||||
s.sub_scope<Float>(Id { "install" }, [&] (Scope<Frame, Vbox, Float> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Float, Vbox> &s) {
|
||||
|
||||
/*
|
||||
* Package is installed but content is missing
|
||||
@ -247,24 +258,18 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
* the pkg's archives.
|
||||
*/
|
||||
if (_blueprint_info.incomplete()) {
|
||||
_gen_info_label(xml, "pad2", "");
|
||||
_gen_info_label(xml, "path", component.path);
|
||||
_gen_info_label(xml, "pad3", "");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "installed but incomplete"); });
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Annotation>(component.path);
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Label>("installed but incomplete");
|
||||
|
||||
if (_nic_ready()) {
|
||||
_gen_info_label(xml, "pad4", "");
|
||||
|
||||
gen_named_node(xml, "float", "install", [&] () {
|
||||
xml.node("button", [&] () {
|
||||
_install_item.gen_button_attr(xml, "install");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "Reattempt Install");
|
||||
});
|
||||
});
|
||||
});
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Float>([&] (Scope<Frame, Vbox, Float, Vbox, Float> &s) {
|
||||
s.widget(_install, [&] (Scope<Button> &s) {
|
||||
s.sub_scope<Label>("Reattempt Install"); }); });
|
||||
}
|
||||
s.sub_scope<Vgap>();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -272,17 +277,13 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
*/
|
||||
else if (_blueprint_info.uninstalled() && _nic_ready()) {
|
||||
|
||||
_gen_pkg_info(xml, component);
|
||||
_gen_info_label(xml, "pad2", "");
|
||||
view_component_info(s, component);
|
||||
|
||||
gen_named_node(xml, "float", "install", [&] () {
|
||||
xml.node("button", [&] () {
|
||||
_install_item.gen_button_attr(xml, "install");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "Install");
|
||||
});
|
||||
});
|
||||
});
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Float>([&] (Scope<Frame, Vbox, Float, Vbox, Float> &s) {
|
||||
s.widget(_install, [&] (Scope<Button> &s) {
|
||||
s.sub_scope<Label>("Install"); }); });
|
||||
s.sub_scope<Vgap>();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -290,20 +291,18 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
* about it
|
||||
*/
|
||||
else if (_blueprint_info.uninstalled()) {
|
||||
_gen_info_label(xml, "pad2", "");
|
||||
_gen_info_label(xml, "path", component.path);
|
||||
_gen_info_label(xml, "pad3", "");
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", "not installed"); });
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Annotation>(component.path);
|
||||
s.sub_scope<Vgap>();
|
||||
s.sub_scope<Label>("not installed");
|
||||
s.sub_scope<Vgap>();
|
||||
}
|
||||
|
||||
_gen_info_label(xml, "pad4", "");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
count++;
|
||||
});
|
||||
}
|
||||
|
||||
@ -312,227 +311,225 @@ void Popup_dialog::_gen_menu_elements(Xml_generator &xml, Xml_node const &depot_
|
||||
*/
|
||||
if (_state >= PKG_SHOWN)
|
||||
_construction_info.with_construction([&] (Component const &component) {
|
||||
_gen_pkg_elements(xml, component); });
|
||||
_view_pkg_elements(s, component); });
|
||||
}
|
||||
|
||||
|
||||
void Popup_dialog::click(Action &action)
|
||||
void Popup_dialog::click(Clicked_at const &at)
|
||||
{
|
||||
Hoverable_item::Id const clicked = _item._hovered;
|
||||
bool clicked_on_back_button = false;
|
||||
|
||||
_action_item .propose_activation_on_click();
|
||||
_install_item.propose_activation_on_click();
|
||||
_pd_route.click();
|
||||
_back.propagate(at, [&] {
|
||||
clicked_on_back_button = true;
|
||||
|
||||
Route::Id const clicked_route = _route_item._hovered;
|
||||
switch (_state) {
|
||||
case TOP_LEVEL: break;
|
||||
case DEPOT_REQUESTED: break;
|
||||
case DEPOT_SHOWN: _state = TOP_LEVEL; break;
|
||||
case DEPOT_SELECTION: _state = DEPOT_SHOWN; break;
|
||||
case INDEX_REQUESTED: break;
|
||||
|
||||
auto back_to_index = [&] ()
|
||||
{
|
||||
_state = INDEX_SHOWN;
|
||||
action.discard_construction();
|
||||
_selected_route.destruct();
|
||||
};
|
||||
|
||||
if (_state == TOP_LEVEL) {
|
||||
|
||||
if (clicked == "depot") {
|
||||
_state = DEPOT_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
} else {
|
||||
action.launch_global(clicked);
|
||||
}
|
||||
}
|
||||
|
||||
else if (_state == DEPOT_SHOWN) {
|
||||
|
||||
/* back to top-level menu */
|
||||
if (clicked == "back") {
|
||||
_state = TOP_LEVEL;
|
||||
|
||||
} else if (clicked == "selection") {
|
||||
_state = DEPOT_SELECTION;
|
||||
|
||||
} else {
|
||||
|
||||
/* enter depot users menu */
|
||||
_selected_user = clicked;
|
||||
_state = INDEX_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
}
|
||||
}
|
||||
|
||||
else if (_state == DEPOT_SELECTION) {
|
||||
|
||||
/* back to depot users */
|
||||
if (clicked == "back") {
|
||||
_state = DEPOT_SHOWN;
|
||||
} else {
|
||||
|
||||
if (!_index_avail(clicked))
|
||||
action.trigger_download(_index_path(clicked), Verify{true});
|
||||
else
|
||||
action.remove_index(clicked);
|
||||
}
|
||||
}
|
||||
|
||||
else if (_state >= INDEX_SHOWN && _state < PKG_SHOWN) {
|
||||
|
||||
/* back to depot users */
|
||||
if (_menu._level == 0 && clicked == "back") {
|
||||
_state = DEPOT_SHOWN;
|
||||
_selected_user = User();
|
||||
} else {
|
||||
|
||||
/* go one menu up */
|
||||
if (clicked == "back") {
|
||||
case INDEX_SHOWN:
|
||||
case PKG_REQUESTED:
|
||||
if (_menu._level > 0) {
|
||||
/* go one menu up */
|
||||
_menu._selected[_menu._level] = Index_menu::Name();
|
||||
_menu._level--;
|
||||
action.discard_construction();
|
||||
_action.discard_construction();
|
||||
} else {
|
||||
|
||||
/* enter sub menu of index */
|
||||
if (_menu._level < Index_menu::MAX_LEVELS - 1) {
|
||||
|
||||
unsigned cnt = 0;
|
||||
_for_each_menu_item([&] (Xml_node item) {
|
||||
|
||||
if (clicked == Hoverable_item::Id(cnt)) {
|
||||
|
||||
if (item.has_type("index")) {
|
||||
|
||||
Index_menu::Name const name =
|
||||
item.attribute_value("name", Index_menu::Name());
|
||||
|
||||
_menu._selected[_menu._level] = name;
|
||||
_menu._level++;
|
||||
|
||||
} else if (item.has_type("pkg")) {
|
||||
|
||||
auto path = item.attribute_value("path", Component::Path());
|
||||
auto info = item.attribute_value("info", Component::Info());
|
||||
|
||||
_construction_name =
|
||||
action.new_construction(path, Verify{true}, info);
|
||||
|
||||
_state = PKG_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
});
|
||||
}
|
||||
_state = DEPOT_SHOWN;
|
||||
_selected_user = User();
|
||||
}
|
||||
break;
|
||||
|
||||
case PKG_SHOWN:
|
||||
case ROUTE_SELECTED:
|
||||
_state = INDEX_SHOWN;
|
||||
_action.discard_construction();
|
||||
_selected_route = { };
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
else if (_state == PKG_SHOWN) {
|
||||
if (clicked_on_back_button)
|
||||
return;
|
||||
|
||||
/* back to index */
|
||||
if (clicked == "back") {
|
||||
back_to_index();
|
||||
_launch.propagate(at);
|
||||
_install.propagate(at);
|
||||
|
||||
Id const route_id = at.matching_id<Frame, Vbox, Frame, Vbox, Menu_entry>();
|
||||
|
||||
auto with_matching_user = [&] (Id const &id, auto const &fn)
|
||||
{
|
||||
unsigned count = 0;
|
||||
_depot_users.xml().for_each_sub_node("user", [&] (Xml_node const &user) {
|
||||
if (id == user_id(count++))
|
||||
fn(user.attribute_value("name", User())); });
|
||||
};
|
||||
|
||||
State const orig_state = _state;
|
||||
|
||||
if (orig_state == TOP_LEVEL) {
|
||||
|
||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
||||
|
||||
if (id.value == "depot") {
|
||||
_state = DEPOT_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
|
||||
} else {
|
||||
|
||||
/* select route to present routing options */
|
||||
if (clicked_route.valid()) {
|
||||
_state = ROUTE_SELECTED;
|
||||
_selected_route.construct(clicked_route);
|
||||
}
|
||||
unsigned count = 0;
|
||||
_launchers.for_each([&] (Launchers::Info const &info) {
|
||||
if (id == launcher_id(count++))
|
||||
_action.launch_global(info.path); });
|
||||
}
|
||||
}
|
||||
|
||||
else if (_state == ROUTE_SELECTED || _dialog_item.hovered("debug")) {
|
||||
else if (orig_state == DEPOT_SHOWN) {
|
||||
|
||||
/*
|
||||
* Keep the routing selection open when clicking on the "Add component"
|
||||
* button. Otherwise, the change of the dialog size (while folding the
|
||||
* route selection) would result in the unhovering of the operaton
|
||||
* button. So the clack would go elsewhere.
|
||||
*/
|
||||
bool const click_on_operation = _action_item.hovered("launch");
|
||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
||||
|
||||
/* back to index */
|
||||
if (clicked == "back") {
|
||||
back_to_index();
|
||||
if (id.value == "selection")
|
||||
_state = DEPOT_SELECTION;
|
||||
|
||||
} else if (!click_on_operation) {
|
||||
/* enter depot users menu */
|
||||
with_matching_user(id, [&] (User const &user) {
|
||||
_selected_user = user;
|
||||
_state = INDEX_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
});
|
||||
}
|
||||
|
||||
/* close selected route */
|
||||
if (clicked_route == "back") {
|
||||
_state = PKG_SHOWN;
|
||||
_selected_route.destruct();
|
||||
_pd_route.reset();
|
||||
else if (orig_state == DEPOT_SELECTION) {
|
||||
|
||||
} else if (_resource_dialog_selected()) {
|
||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
||||
|
||||
bool const clicked_on_different_route = clicked_route.valid()
|
||||
&& (clicked_route != "");
|
||||
if (clicked_on_different_route) {
|
||||
with_matching_user(id, [&] (User const &user) {
|
||||
|
||||
/* close resource dialog */
|
||||
_selected_route.construct(clicked_route);
|
||||
if (!_index_avail(user))
|
||||
_action.trigger_download(_index_path(user), Verify{true});
|
||||
else
|
||||
_action.remove_index(user);
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
else if (orig_state >= INDEX_SHOWN && orig_state < PKG_SHOWN) {
|
||||
|
||||
if (_resources.constructed())
|
||||
action.apply_to_construction([&] (Component &component) {
|
||||
_resources->click(component); });
|
||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
||||
|
||||
/* enter sub menu of index */
|
||||
if (_menu._level < Index_menu::MAX_LEVELS - 1) {
|
||||
|
||||
unsigned count = 0;
|
||||
_for_each_menu_item([&] (Xml_node item) {
|
||||
|
||||
if (id.value == Id::Value(count)) {
|
||||
|
||||
if (item.has_type("index")) {
|
||||
|
||||
Index_menu::Name const name =
|
||||
item.attribute_value("name", Index_menu::Name());
|
||||
|
||||
_menu._selected[_menu._level] = name;
|
||||
_menu._level++;
|
||||
|
||||
} else if (item.has_type("pkg")) {
|
||||
|
||||
auto path = item.attribute_value("path", Component::Path());
|
||||
auto info = item.attribute_value("info", Component::Info());
|
||||
|
||||
_construction_name =
|
||||
_action.new_construction(path, Verify{true}, info);
|
||||
|
||||
_state = PKG_REQUESTED;
|
||||
_depot_query.trigger_depot_query();
|
||||
}
|
||||
}
|
||||
count++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
else if (orig_state == PKG_SHOWN) {
|
||||
|
||||
/* select route to present routing options */
|
||||
if (route_id.valid()) {
|
||||
_state = ROUTE_SELECTED;
|
||||
_selected_route = route_id;
|
||||
}
|
||||
}
|
||||
|
||||
else if (orig_state == ROUTE_SELECTED) {
|
||||
|
||||
/* close selected route */
|
||||
if (route_id.value == "back") {
|
||||
_state = PKG_SHOWN;
|
||||
_selected_route = { };
|
||||
|
||||
} else if (_resource_dialog_selected()) {
|
||||
|
||||
bool const clicked_on_different_route = route_id.valid();
|
||||
if (clicked_on_different_route) {
|
||||
_selected_route = route_id;
|
||||
|
||||
} else {
|
||||
|
||||
bool clicked_on_selected_route = false;
|
||||
_action.apply_to_construction([&] (Component &component) {
|
||||
_resources.propagate(at, component); });
|
||||
}
|
||||
|
||||
_apply_to_selected_route(action, [&] (Route &route) {
|
||||
} else {
|
||||
|
||||
unsigned cnt = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
bool clicked_on_selected_route = false;
|
||||
|
||||
Hoverable_item::Id const id("service.", cnt++);
|
||||
_apply_to_selected_route(_action, [&] (Route &route) {
|
||||
|
||||
if (clicked_route == id) {
|
||||
unsigned count = 0;
|
||||
_runtime_config.for_each_service([&] (Service const &service) {
|
||||
|
||||
bool const clicked_service_already_selected =
|
||||
route.selected_service.constructed() &&
|
||||
id == route.selected_service_id;
|
||||
Id const id { Id::Value("service.", count++) };
|
||||
|
||||
if (clicked_service_already_selected) {
|
||||
if (route_id == id) {
|
||||
|
||||
/* clear selection */
|
||||
route.selected_service.destruct();
|
||||
route.selected_service_id = Hoverable_item::Id();
|
||||
bool const clicked_service_already_selected =
|
||||
route.selected_service.constructed() &&
|
||||
id.value == route.selected_service_id;
|
||||
|
||||
} else {
|
||||
if (clicked_service_already_selected) {
|
||||
|
||||
/* select different service */
|
||||
route.selected_service.construct(service);
|
||||
route.selected_service_id = id;
|
||||
}
|
||||
/* clear selection */
|
||||
route.selected_service.destruct();
|
||||
route.selected_service_id = { };
|
||||
|
||||
_state = PKG_SHOWN;
|
||||
_selected_route.destruct();
|
||||
} else {
|
||||
|
||||
clicked_on_selected_route = true;
|
||||
/* select different service */
|
||||
route.selected_service.construct(service);
|
||||
route.selected_service_id = id.value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* select different route */
|
||||
if (!clicked_on_selected_route && clicked_route.valid()) {
|
||||
_state = ROUTE_SELECTED;
|
||||
_selected_route.construct(clicked_route);
|
||||
}
|
||||
_state = PKG_SHOWN;
|
||||
_selected_route = { };
|
||||
|
||||
action.apply_to_construction([&] (Component &component) {
|
||||
_pd_route.click(component);
|
||||
clicked_on_selected_route = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (_selected_route == _pd_route.id)
|
||||
_action.apply_to_construction([&] (Component &component) {
|
||||
_pd_route.propagate(at, component); });
|
||||
|
||||
/* select different route */
|
||||
if (!clicked_on_selected_route && route_id.valid()) {
|
||||
_state = ROUTE_SELECTED;
|
||||
_selected_route = route_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_state == PKG_SHOWN || _state == ROUTE_SELECTED) {
|
||||
if (_dialog_item.hovered("debug"))
|
||||
action.apply_to_construction([&] (Component &component) {
|
||||
_debug.click(component); });
|
||||
}
|
||||
if (orig_state == PKG_SHOWN || orig_state == ROUTE_SELECTED)
|
||||
_action.apply_to_construction([&] (Component &component) {
|
||||
_debug.propagate(at, component); });
|
||||
}
|
||||
|
@ -14,10 +14,6 @@
|
||||
#ifndef _VIEW__POPUP_DIALOG_H_
|
||||
#define _VIEW__POPUP_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/reporter.h>
|
||||
#include <depot/archive.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <model/launchers.h>
|
||||
@ -28,17 +24,15 @@
|
||||
#include <model/nic_state.h>
|
||||
#include <model/index_menu.h>
|
||||
#include <view/dialog.h>
|
||||
#include <view/activatable_item.h>
|
||||
#include <view/pd_route_widget.h>
|
||||
#include <view/resource_widget.h>
|
||||
#include <view/debug_widget.h>
|
||||
#include <depot_query.h>
|
||||
|
||||
#include <view/pd_route_dialog.h>
|
||||
#include <view/resource_dialog.h>
|
||||
#include <view/debug_dialog.h>
|
||||
|
||||
namespace Sculpt { struct Popup_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
struct Sculpt::Popup_dialog : Dialog::Top_level_dialog
|
||||
{
|
||||
using Depot_users = Attached_rom_dataspace;
|
||||
using Blueprint_info = Component::Blueprint_info;
|
||||
@ -115,18 +109,40 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
virtual void remove_index(Depot::Archive::User const &) = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
|
||||
Construction_info const &_construction_info;
|
||||
|
||||
Hoverable_item _item { };
|
||||
Activatable_item _action_item { };
|
||||
Activatable_item _install_item { };
|
||||
Hoverable_item _route_item { };
|
||||
Hoverable_item _dialog_item { }; /* for detecting clicks into debug dialog */
|
||||
Pd_route_dialog _pd_route { _runtime_config };
|
||||
using Route_entry = Hosted<Frame, Vbox, Frame, Vbox, Menu_entry>;
|
||||
using Service_entry = Hosted<Frame, Vbox, Frame, Vbox, Menu_entry>;
|
||||
|
||||
Constructible<Resource_dialog> _resources { };
|
||||
struct Sub_menu_title : Widget<Left_floating_hbox>
|
||||
{
|
||||
void view(Scope<Left_floating_hbox> &s, auto const &text) const
|
||||
{
|
||||
bool const hovered = (s.hovered() && !s.dragged());
|
||||
|
||||
Debug_dialog _debug { };
|
||||
s.sub_scope<Icon>("back", Icon::Attr { .hovered = hovered,
|
||||
.selected = true });
|
||||
s.sub_scope<Label>(" ");
|
||||
s.sub_scope<Label>(text, [&] (auto &s) {
|
||||
s.attribute("font", "title/regular"); });
|
||||
|
||||
/* inflate vertical space to button size */
|
||||
s.sub_scope<Button>([&] (Scope<Left_floating_hbox, Button> &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_scope<Label>(""); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &, auto const &fn) { fn(); }
|
||||
};
|
||||
|
||||
Hosted<Frame, Vbox, Sub_menu_title> _back { Id { "back" } };
|
||||
Hosted<Frame, Vbox, Deferred_action_button> _launch { Id { "Add component" } };
|
||||
Hosted<Frame, Vbox, Float, Vbox, Float, Deferred_action_button> _install { Id { "install" } };
|
||||
Hosted<Frame, Vbox, Frame, Vbox, Resource_widget> _resources { Id { "resources" } };
|
||||
Hosted<Frame, Vbox, Frame, Pd_route_widget> _pd_route { Id { "pd_route" }, _runtime_config };
|
||||
Hosted<Frame, Vbox, Frame, Debug_widget> _debug { Id { "debug" } };
|
||||
|
||||
enum State { TOP_LEVEL, DEPOT_REQUESTED, DEPOT_SHOWN, DEPOT_SELECTION,
|
||||
INDEX_REQUESTED, INDEX_SHOWN,
|
||||
@ -141,11 +157,11 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
|
||||
Component::Name _construction_name { };
|
||||
|
||||
Constructible<Route::Id> _selected_route { };
|
||||
Id _selected_route { };
|
||||
|
||||
bool _route_selected(Route::Id const &id) const
|
||||
{
|
||||
return _selected_route.constructed() && id == _selected_route->string();
|
||||
return _selected_route.valid() && id == _selected_route.value;
|
||||
}
|
||||
|
||||
bool _resource_dialog_selected() const
|
||||
@ -153,8 +169,7 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
return _route_selected("resources");
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void _apply_to_selected_route(Action &action, FN const &fn)
|
||||
void _apply_to_selected_route(Action &action, auto const &fn)
|
||||
{
|
||||
unsigned cnt = 0;
|
||||
action.apply_to_construction([&] (Component &component) {
|
||||
@ -165,29 +180,6 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
|
||||
Index_menu _menu { };
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
Deprecated_dialog::Hover_result hover_result = Deprecated_dialog::any_hover_changed(
|
||||
_item .match(hover, "frame", "vbox", "hbox", "name"),
|
||||
_action_item .match(hover, "frame", "vbox", "button", "name"),
|
||||
_install_item.match(hover, "frame", "vbox", "float", "vbox", "float", "button", "name"),
|
||||
_route_item .match(hover, "frame", "vbox", "frame", "vbox", "hbox", "name"),
|
||||
_dialog_item .match(hover, "frame", "vbox", "frame", "name"));
|
||||
|
||||
_pd_route.hover(hover, "frame", "vbox", "frame", "vbox", "hbox", "name");
|
||||
|
||||
if (_resources.constructed())
|
||||
hover_result = Deprecated_dialog::any_hover_changed(
|
||||
hover_result,
|
||||
_resources->match_sub_dialog(hover, "frame", "vbox", "frame", "vbox"));
|
||||
|
||||
hover_result = Deprecated_dialog::any_hover_changed(
|
||||
hover_result,
|
||||
_debug.match_sub_dialog(hover, "frame", "vbox", "frame", "vbox"));
|
||||
|
||||
return hover_result;
|
||||
}
|
||||
|
||||
void depot_users_scan_updated()
|
||||
{
|
||||
if (_state == DEPOT_REQUESTED)
|
||||
@ -230,153 +222,54 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
return Path(user, "/index/", _sculpt_version);
|
||||
}
|
||||
|
||||
void _gen_sub_menu_title(Xml_generator &xml,
|
||||
Start_name const &name,
|
||||
Start_name const &text) const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "back", [&] () {
|
||||
xml.attribute("selected", "yes");
|
||||
xml.attribute("style", "back");
|
||||
_item.gen_hovered_attr(xml, name);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "label", [&] () {
|
||||
xml.attribute("font", "title/regular");
|
||||
xml.attribute("text", Path(" ", text));
|
||||
});
|
||||
});
|
||||
});
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_menu_entry(Xml_generator &xml, Start_name const &name,
|
||||
Component::Info const &text, bool selected,
|
||||
char const *style = "radio") const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", style);
|
||||
_item.gen_hovered_attr(xml, name);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_route_entry(Xml_generator &xml,
|
||||
Start_name const &name,
|
||||
Start_name const &text,
|
||||
bool selected, char const *style = "radio") const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", style);
|
||||
_route_item.gen_hovered_attr(xml, name);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void _for_each_menu_item(FN const &fn) const
|
||||
{
|
||||
_menu.for_each_item(_index_rom.xml(), _selected_user, fn);
|
||||
}
|
||||
|
||||
static void _gen_info_label(Xml_generator &xml, char const *name,
|
||||
Component::Info const &info)
|
||||
void _view_pkg_elements (Scope<Frame, Vbox> &, Component const &) const;
|
||||
void _view_menu_elements(Scope<Frame, Vbox> &, Xml_node const &depot_users) const;
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
gen_named_node(xml, "label", name, [&] () {
|
||||
xml.attribute("font", "annotation/regular");
|
||||
xml.attribute("text", Component::Info(" ", info, " ")); });
|
||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||
_view_menu_elements(s, _depot_users.xml()); }); });
|
||||
}
|
||||
|
||||
void _gen_pkg_info (Xml_generator &, Component const &) const;
|
||||
void _gen_pkg_elements (Xml_generator &, Component const &) const;
|
||||
void _gen_menu_elements(Xml_generator &, Xml_node const &depot_users) const;
|
||||
void click(Clicked_at const &) override;
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
void clack(Clacked_at const &at) override
|
||||
{
|
||||
xml.node("frame", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
_gen_menu_elements(xml, _depot_users.xml()); }); });
|
||||
}
|
||||
|
||||
void click(Action &action);
|
||||
|
||||
void clack(Action &action)
|
||||
{
|
||||
_action_item.confirm_activation_on_clack();
|
||||
_install_item.confirm_activation_on_clack();
|
||||
|
||||
if (_action_item.activated("launch")) {
|
||||
action.launch_construction();
|
||||
_launch.propagate(at, [&] {
|
||||
_action.launch_construction();
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
bool const pkg_need_install = !_blueprint_info.pkg_avail
|
||||
|| _blueprint_info.incomplete();
|
||||
|
||||
if (pkg_need_install && _install_item.activated("install")) {
|
||||
_construction_info.with_construction([&] (Component const &component) {
|
||||
action.trigger_download(component.path, component.verify);
|
||||
_install_item.reset();
|
||||
_refresh.refresh_popup_dialog();
|
||||
});
|
||||
}
|
||||
_install.propagate(at, [&] {
|
||||
bool const pkg_need_install = !_blueprint_info.pkg_avail
|
||||
|| _blueprint_info.incomplete();
|
||||
if (pkg_need_install) {
|
||||
_construction_info.with_construction([&] (Component const &component) {
|
||||
_action.trigger_download(component.path, component.verify);
|
||||
_refresh.refresh_popup_dialog();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void reset() override
|
||||
void drag(Dragged_at const &) override { }
|
||||
|
||||
void reset()
|
||||
{
|
||||
_item._hovered = Hoverable_item::Id();
|
||||
_route_item._hovered = Hoverable_item::Id();
|
||||
_dialog_item._hovered = Hoverable_item::Id();
|
||||
_action_item.reset();
|
||||
_install_item.reset();
|
||||
_state = TOP_LEVEL;
|
||||
_selected_user = User();
|
||||
_selected_route.destruct();
|
||||
_selected_route = { };
|
||||
_menu._level = 0;
|
||||
_resources.destruct();
|
||||
_debug.reset();
|
||||
_pd_route.reset();
|
||||
}
|
||||
|
||||
Popup_dialog(Env &env, Refresh &refresh,
|
||||
Popup_dialog(Env &env, Refresh &refresh, Action &action,
|
||||
Launchers const &launchers,
|
||||
Nic_state const &nic_state,
|
||||
Nic_target const &nic_target,
|
||||
@ -387,12 +280,13 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
Depot_query &depot_query,
|
||||
Construction_info const &construction_info)
|
||||
:
|
||||
Top_level_dialog("popup"),
|
||||
_env(env), _launchers(launchers),
|
||||
_nic_state(nic_state), _nic_target(nic_target),
|
||||
_runtime_info(runtime_info), _runtime_config(runtime_config),
|
||||
_download_queue(download_queue), _depot_users(depot_users),
|
||||
_depot_query(depot_query),
|
||||
_refresh(refresh), _construction_info(construction_info)
|
||||
_depot_query(depot_query), _refresh(refresh), _action(action),
|
||||
_construction_info(construction_info)
|
||||
{
|
||||
_index_rom.sigh(_index_handler);
|
||||
}
|
||||
@ -423,12 +317,6 @@ struct Sculpt::Popup_dialog : Deprecated_dialog
|
||||
if (_state < PKG_REQUESTED)
|
||||
return;
|
||||
|
||||
_resources.construct(construction.affinity_space,
|
||||
construction.affinity_location,
|
||||
construction.priority);
|
||||
|
||||
_debug.reset();
|
||||
|
||||
construction.try_apply_blueprint(blueprint);
|
||||
|
||||
_blueprint_info = construction.blueprint_info;
|
||||
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* \brief Radio-button dialog
|
||||
* \author Norman Feske
|
||||
* \date 2021-03-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__RADIO_CHOICE_DIALOG_H_
|
||||
#define _VIEW__RADIO_CHOICE_DIALOG_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Radio_choice_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Radio_choice_dialog : Noncopyable, Deprecated_dialog
|
||||
{
|
||||
typedef Hoverable_item::Id Id;
|
||||
|
||||
Id const _id;
|
||||
|
||||
struct Min_ex { unsigned left; unsigned right; };
|
||||
|
||||
Min_ex const _min_ex;
|
||||
|
||||
Hoverable_item _choice_item { };
|
||||
|
||||
bool _unfolded = false;
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
return any_hover_changed(_choice_item.match(hover, "hbox", "frame", "vbox", "hbox", "name"));
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
_unfolded = false;
|
||||
}
|
||||
|
||||
struct Choice : Interface, Noncopyable
|
||||
{
|
||||
virtual void generate(Id const &option_id) const = 0;
|
||||
};
|
||||
|
||||
void generate(Xml_generator &) const override { };
|
||||
|
||||
template <typename FN>
|
||||
void generate(Xml_generator &xml, Id const &selected_id, FN const &fn) const
|
||||
{
|
||||
struct Choice_generator : Choice
|
||||
{
|
||||
Xml_generator &xml;
|
||||
|
||||
Radio_choice_dialog const &dialog;
|
||||
|
||||
Id const selected_id;
|
||||
|
||||
Choice_generator(Xml_generator &xml, Id const &selected_id,
|
||||
Radio_choice_dialog const &dialog)
|
||||
:
|
||||
xml(xml), dialog(dialog), selected_id(selected_id)
|
||||
{ }
|
||||
|
||||
void generate(Id const &option_id) const override
|
||||
{
|
||||
bool const selected = (option_id == selected_id);
|
||||
|
||||
if (!selected && !dialog._unfolded)
|
||||
return;
|
||||
|
||||
gen_named_node(xml, "hbox", option_id, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", "radio");
|
||||
dialog._choice_item.gen_hovered_attr(xml, option_id);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", option_id)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
} choice_generator { xml, selected_id, *this };
|
||||
|
||||
gen_named_node(xml, "hbox", _id, [&] () {
|
||||
gen_named_node(xml, "vbox", "left", [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "title", [&] () {
|
||||
xml.attribute("north", true);
|
||||
xml.attribute("west", true);
|
||||
xml.node("frame", [&] () {
|
||||
xml.attribute("style", "invisible");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", _id); }); });
|
||||
|
||||
/* used for consistent vertical text alignment */
|
||||
gen_named_node(xml, "button", "vspace", [&] () {
|
||||
xml.attribute("style", "invisible");
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "label", "hspace", [&] () {
|
||||
xml.attribute("min_ex", _min_ex.left); });
|
||||
});
|
||||
|
||||
gen_named_node(xml, "frame", "right", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
fn(choice_generator);
|
||||
|
||||
gen_named_node(xml, "label", "hspace", [&] () {
|
||||
xml.attribute("min_ex", _min_ex.right); });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Click_result click()
|
||||
{
|
||||
/* unfold choice on click */
|
||||
if (_choice_item._hovered == "")
|
||||
return Click_result::IGNORED;
|
||||
|
||||
_unfolded = true;
|
||||
return Click_result::CONSUMED;
|
||||
}
|
||||
|
||||
Id hovered_choice() const { return _choice_item._hovered; }
|
||||
|
||||
Radio_choice_dialog(Id const &id, Min_ex min_ex)
|
||||
:
|
||||
_id(id), _min_ex(min_ex)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__RADIO_CHOICE_DIALOG_H_ */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief RAM file-system management dialog
|
||||
* \brief RAM file-system management widget
|
||||
* \author Norman Feske
|
||||
* \date 2020-01-28
|
||||
*/
|
||||
@ -11,16 +11,16 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__RAM_FS_DIALOG_H_
|
||||
#define _VIEW__RAM_FS_DIALOG_H_
|
||||
#ifndef _VIEW__RAM_FS_WIDGET_H_
|
||||
#define _VIEW__RAM_FS_WIDGET_H_
|
||||
|
||||
#include <view/fs_operations.h>
|
||||
#include <model/ram_fs_state.h>
|
||||
|
||||
namespace Sculpt { struct Ram_fs_dialog; }
|
||||
namespace Sculpt { struct Ram_fs_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Ram_fs_dialog : Widget<Vbox>
|
||||
struct Sculpt::Ram_fs_widget : Widget<Vbox>
|
||||
{
|
||||
Storage_target const _target { "ram_fs", Partition::Number() };
|
||||
|
||||
@ -59,4 +59,4 @@ struct Sculpt::Ram_fs_dialog : Widget<Vbox>
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__RAM_FS_DIALOG_H_ */
|
||||
#endif /* _VIEW__RAM_FS_WIDGET_H_ */
|
@ -1,290 +0,0 @@
|
||||
/*
|
||||
* \brief Resource assignment dialog
|
||||
* \author Alexander Boettcher
|
||||
* \author Norman Feske
|
||||
* \date 2020-07-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020-2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <view/resource_dialog.h>
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
|
||||
void Resource_dialog::_gen_affinity_section(Xml_generator &xml) const
|
||||
{
|
||||
unsigned const CELL_WIDTH_EX = 4;
|
||||
|
||||
using Id = Hoverable_item::Id;
|
||||
|
||||
auto gen_small_label = [] (Xml_generator &xml, auto id, auto fn) {
|
||||
gen_named_node(xml, "label", id, [&] () {
|
||||
xml.attribute("font", "annotation/regular");
|
||||
fn(); }); };
|
||||
|
||||
auto gen_cell_hspacer = [&] (Xml_generator &xml, auto id) {
|
||||
gen_small_label(xml, id, [&] () {
|
||||
xml.attribute("min_ex", CELL_WIDTH_EX); }); };
|
||||
|
||||
auto gen_cell_cpu = [&] (Xml_generator &xml, auto name, bool selected)
|
||||
{
|
||||
gen_named_node(xml, "vbox", name, [&] () {
|
||||
|
||||
gen_named_node(xml, "button", name, [&] () {
|
||||
xml.attribute("style", "checkbox");
|
||||
_space_item.gen_hovered_attr(xml, name);
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_cell_hspacer(xml, "below");
|
||||
});
|
||||
};
|
||||
|
||||
auto gen_cell_number = [&] (Xml_generator &xml, Id n)
|
||||
{
|
||||
gen_named_node(xml, "vbox", n, [&] () {
|
||||
gen_cell_hspacer(xml, "above");
|
||||
gen_named_node(xml, "float", "number", [&] () {
|
||||
gen_small_label(xml, "number", [&] () {
|
||||
xml.attribute("text", n); }); });
|
||||
gen_cell_hspacer(xml, "below");
|
||||
});
|
||||
};
|
||||
|
||||
auto cpu_selected = [] (Affinity::Location location, unsigned x, unsigned y)
|
||||
{
|
||||
return (unsigned(location.xpos()) <= x)
|
||||
&& (x < location.xpos() + location.width())
|
||||
&& (unsigned(location.ypos()) <= y)
|
||||
&& (y < location.ypos() + location.height());
|
||||
};
|
||||
|
||||
_gen_dialog_section(xml, "affinity", "Affinity", [&] () {
|
||||
|
||||
gen_named_node(xml, "vbox", "selection", [&] () {
|
||||
|
||||
bool const have_hyperthreads = (_space.height() > 1);
|
||||
|
||||
gen_named_node(xml, "hbox", "labeledcores", [&] () {
|
||||
gen_named_node(xml, "vbox", "cores", [&] () {
|
||||
|
||||
for (unsigned y = 0; y < _space.height(); y++) {
|
||||
|
||||
gen_named_node(xml, "hbox", Id("row", y), [&] () {
|
||||
|
||||
for (unsigned x = 0; x < _space.width(); x++)
|
||||
gen_cell_cpu(xml, _cpu_id(x, y),
|
||||
cpu_selected(_location, x, y));
|
||||
|
||||
if (have_hyperthreads)
|
||||
gen_named_node(xml, "float", "number", [&] () {
|
||||
gen_small_label(xml, "number", [&] () {
|
||||
xml.attribute("min_ex", 2);
|
||||
xml.attribute("text", y); }); });
|
||||
});
|
||||
}
|
||||
});
|
||||
if (have_hyperthreads) {
|
||||
gen_named_node(xml, "float", "hyperthreadslabel", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
unsigned line = 0;
|
||||
|
||||
auto gen_leftaligned = [&] (auto text) {
|
||||
gen_named_node(xml, "float", Id(line++), [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
gen_small_label(xml, "label", [&] () {
|
||||
xml.attribute("text", text); }); }); };
|
||||
|
||||
gen_leftaligned("Hyper");
|
||||
gen_leftaligned("threads");
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
gen_named_node(xml, "float", "corelabels", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
for (unsigned x = 0; x < _space.width(); x++)
|
||||
gen_cell_number(xml, x); });
|
||||
|
||||
gen_small_label(xml, "cores", [&] () {
|
||||
xml.attribute("text", "Cores"); });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Resource_dialog::_gen_priority_section(Xml_generator &xml) const
|
||||
{
|
||||
_gen_dialog_section(xml, "priority", "Priority", [&] () {
|
||||
|
||||
gen_named_node(xml, "vbox", "selection", [&] () {
|
||||
|
||||
auto gen_radiobutton = [&] (auto id, auto text)
|
||||
{
|
||||
gen_named_node(xml, "hbox", id, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
gen_named_node(xml, "hbox", id, [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
xml.attribute("style", "radio");
|
||||
_priority_item.gen_button_attr(xml, id);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", text);
|
||||
xml.attribute("min_ex", 13);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
};
|
||||
|
||||
gen_radiobutton("driver", "Driver");
|
||||
gen_radiobutton("multimedia", "Multimedia");
|
||||
gen_radiobutton("default", "Default");
|
||||
gen_radiobutton("background", "Background");
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Resource_dialog::click(Component &component)
|
||||
{
|
||||
if (component.affinity_space.total() > 1) {
|
||||
Hoverable_item::Id const clicked_space = _space_item._hovered;
|
||||
if (clicked_space.valid()) {
|
||||
_click_space(component, clicked_space);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Hoverable_item::Id const clicked_priority = _priority_item._hovered;
|
||||
if (clicked_priority.valid()) {
|
||||
_click_priority(component, clicked_priority);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_option_item.hovered("system_control")) {
|
||||
_system_control = !_system_control;
|
||||
component.system_control = _system_control;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Resource_dialog::_click_space(Component &component, Hoverable_item::Id clicked_space)
|
||||
{
|
||||
for (unsigned y = 0; y < _space.height(); y++) {
|
||||
for (unsigned x = 0; x < _space.width(); x++) {
|
||||
if (_cpu_id(x, y) != clicked_space)
|
||||
continue;
|
||||
|
||||
unsigned loc_x = _location.xpos();
|
||||
unsigned loc_y = _location.ypos();
|
||||
unsigned loc_w = _location.width();
|
||||
unsigned loc_h = _location.height();
|
||||
|
||||
bool handled_x = false;
|
||||
bool handled_y = false;
|
||||
|
||||
if (x < loc_x) {
|
||||
loc_w += loc_x - x;
|
||||
loc_x = x;
|
||||
handled_x = true;
|
||||
} else if (x >= loc_x + loc_w) {
|
||||
loc_w = x - loc_x + 1;
|
||||
handled_x = true;
|
||||
}
|
||||
|
||||
if (y < loc_y) {
|
||||
loc_h += loc_y - y;
|
||||
loc_y = y;
|
||||
handled_y = true;
|
||||
} else if (y >= loc_y + loc_h) {
|
||||
loc_h = y - loc_y + 1;
|
||||
handled_y = true;
|
||||
}
|
||||
|
||||
if (handled_x && !handled_y) {
|
||||
handled_y = true;
|
||||
} else
|
||||
if (handled_y && !handled_x) {
|
||||
handled_x = true;
|
||||
}
|
||||
|
||||
if (!handled_x) {
|
||||
if ((x - loc_x) < (loc_x + loc_w - x)) {
|
||||
if (x - loc_x + 1 < loc_w) {
|
||||
loc_w -= x - loc_x + 1;
|
||||
loc_x = x + 1;
|
||||
} else {
|
||||
loc_w = loc_x + loc_w - x;
|
||||
loc_x = x;
|
||||
}
|
||||
} else {
|
||||
loc_w = x - loc_x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled_y) {
|
||||
if ((y - loc_y) < (loc_y + loc_h - y)) {
|
||||
if (y - loc_y + 1 < loc_h) {
|
||||
loc_h -= y - loc_y + 1;
|
||||
loc_y = y + 1;
|
||||
} else {
|
||||
loc_h = loc_y + loc_h - y;
|
||||
loc_y = y;
|
||||
}
|
||||
} else {
|
||||
loc_h = y - loc_y;
|
||||
}
|
||||
}
|
||||
|
||||
_location = Affinity::Location(loc_x, loc_y,
|
||||
loc_w, loc_h);
|
||||
|
||||
component.affinity_location = _location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Resource_dialog::_click_priority(Component &component, Hoverable_item::Id priority)
|
||||
{
|
||||
_priority_item.select(priority);
|
||||
|
||||
/* propagate priority change to component */
|
||||
auto priority_value = [] (Hoverable_item::Id string)
|
||||
{
|
||||
if (string == "background") return Priority::BACKGROUND;
|
||||
if (string == "default") return Priority::DEFAULT;
|
||||
if (string == "multimedia") return Priority::MULTIMEDIA;
|
||||
if (string == "driver") return Priority::DRIVER;
|
||||
|
||||
return Priority::BACKGROUND;
|
||||
};
|
||||
|
||||
component.priority = priority_value(priority);
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
/*
|
||||
* \brief Resource assignment dialog
|
||||
* \author Alexander Boettcher
|
||||
* \author Norman Feske
|
||||
* \date 2020-07-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020-2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__RESOURCE_DIALOG_H_
|
||||
#define _VIEW__RESOURCE_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/reporter.h>
|
||||
#include <depot/archive.h>
|
||||
|
||||
/* local includes */
|
||||
#include <xml.h>
|
||||
#include <model/component.h>
|
||||
#include <view/dialog.h>
|
||||
#include <view/selectable_item.h>
|
||||
|
||||
namespace Sculpt { struct Resource_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Resource_dialog : Noncopyable, Deprecated_dialog
|
||||
{
|
||||
Affinity::Space const _space;
|
||||
Affinity::Location _location;
|
||||
Hoverable_item _space_item { };
|
||||
Selectable_item _priority_item { };
|
||||
Hoverable_item _option_item { };
|
||||
|
||||
bool _system_control = false;
|
||||
|
||||
static char const *_priority_id(Priority priority)
|
||||
{
|
||||
switch (priority) {
|
||||
case Priority::DRIVER: return "driver";
|
||||
case Priority::MULTIMEDIA: return "multimedia";
|
||||
case Priority::BACKGROUND: return "background";
|
||||
default: break;
|
||||
};
|
||||
return "default";
|
||||
}
|
||||
|
||||
static Hoverable_item::Id _cpu_id(unsigned x, unsigned y)
|
||||
{
|
||||
return Hoverable_item::Id("cpu", x, "x", y);
|
||||
}
|
||||
|
||||
Resource_dialog(Affinity::Space space, Affinity::Location location,
|
||||
Priority priority)
|
||||
:
|
||||
_space(space), _location(location)
|
||||
{
|
||||
_priority_item.select(_priority_id(priority));
|
||||
}
|
||||
|
||||
void _gen_affinity_section(Xml_generator &) const;
|
||||
void _gen_priority_section(Xml_generator &) const;
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
return Deprecated_dialog::any_hover_changed(
|
||||
_space_item.match(hover,
|
||||
"vbox", "float", "hbox", /* _gen_dialog_section */
|
||||
"vbox", "hbox", "vbox", "hbox", /* _gen_affinity_section */
|
||||
"vbox", "button", "name" /* gen_cell_cpu */
|
||||
),
|
||||
_priority_item.match(hover,
|
||||
"vbox", "float", "hbox", /* _gen_dialog_section */
|
||||
"vbox", "hbox", "float", "hbox", "name"),
|
||||
_option_item.match(hover, "vbox", "hbox", "name")
|
||||
);
|
||||
}
|
||||
|
||||
void click(Component &);
|
||||
|
||||
void _click_space (Component &, Hoverable_item::Id);
|
||||
void _click_priority(Component &, Hoverable_item::Id);
|
||||
|
||||
template <typename FN>
|
||||
void _gen_dialog_section(Xml_generator &xml, Hoverable_item::Id id,
|
||||
char const *text, FN const &gen_content_fn) const
|
||||
{
|
||||
gen_named_node(xml, "float", id, [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
|
||||
gen_named_node(xml, "vbox", "title", [&] () {
|
||||
xml.node("float", [&] () {
|
||||
|
||||
xml.attribute("north", "yes");
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
|
||||
/*
|
||||
* The button is used to vertically align the "Priority"
|
||||
* label with the text of the first radio button.
|
||||
* The leading space is used to horizontally align
|
||||
* the label with the "Resource assignment ..." dialog
|
||||
* title.
|
||||
*/
|
||||
gen_named_node(xml, "button", "spacer", [&] () {
|
||||
xml.attribute("style", "invisible");
|
||||
xml.node("hbox", [&] () { }); });
|
||||
|
||||
gen_named_node(xml, "label", "label", [&] () {
|
||||
xml.attribute("text", String<32>(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "label", "spacer", [&] () {
|
||||
xml.attribute("min_ex", 11); });
|
||||
});
|
||||
|
||||
gen_content_fn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_option(Xml_generator &xml, auto const &name, auto const &text, bool selected) const
|
||||
{
|
||||
gen_named_node(xml, "hbox", name, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
|
||||
/* align with the "Resource assignment ..." dialog */
|
||||
gen_named_node(xml, "button", "left", [&] () {
|
||||
xml.attribute("style", "invisible");
|
||||
xml.node("hbox", [&] () { }); });
|
||||
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
if (selected)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", "checkbox");
|
||||
_option_item.gen_hovered_attr(xml, name);
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Path(" ", text)); });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "hbox", "right", [&] () { });
|
||||
});
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
{
|
||||
auto gen_vspacer = [&] (auto id) {
|
||||
gen_named_node(xml, "label", id, [&] () {
|
||||
xml.attribute("text", "");
|
||||
xml.attribute("font", "annotation/regular"); }); };
|
||||
|
||||
xml.node("vbox", [&] () {
|
||||
gen_vspacer("spacer1");
|
||||
_gen_affinity_section(xml);
|
||||
gen_vspacer("spacer2");
|
||||
_gen_priority_section(xml);
|
||||
gen_vspacer("spacer3");
|
||||
_gen_option(xml, "system_control", "System control", _system_control);
|
||||
gen_vspacer("spacer4");
|
||||
});
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
_space_item._hovered = Hoverable_item::Id();
|
||||
_priority_item.reset();
|
||||
_location = Affinity::Location();
|
||||
_system_control = false;
|
||||
_option_item._hovered = Hoverable_item::Id();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__RESOURCE_DIALOG_H_ */
|
173
repos/gems/src/app/sculpt_manager/view/resource_widget.cc
Normal file
173
repos/gems/src/app/sculpt_manager/view/resource_widget.cc
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* \brief Resource-assignment widget
|
||||
* \author Alexander Boettcher
|
||||
* \author Norman Feske
|
||||
* \date 2020-07-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020-2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <view/resource_widget.h>
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
|
||||
void Resource_widget::Affinity_selector::view(Scope<Vbox> &s,
|
||||
Affinity::Space const &space,
|
||||
Affinity::Location const &location) const
|
||||
{
|
||||
auto view_hyperthread_index = [] (auto &s, unsigned index) {
|
||||
s.template sub_scope<Label>(String<8>(index), [&] (auto &s) {
|
||||
s.attribute("font", "annotation/regular");
|
||||
s.attribute("min_ex", 2); }); };
|
||||
|
||||
auto view_cell_hspacer = [] (auto &s) {
|
||||
s.template sub_scope<Min_ex>(5); };
|
||||
|
||||
auto view_cell_cpu = [&] (auto &s, Id const &id, bool selected) {
|
||||
s.template sub_scope<Vbox>(id, [&] (auto &s) {
|
||||
view_cell_hspacer(s);
|
||||
s.template sub_scope<Float>(id, [&] (auto &s) {
|
||||
s.template sub_scope<Button>([&] (auto &s) {
|
||||
s.attribute("style", "checkbox");
|
||||
s.attribute("selected", selected);
|
||||
s.template sub_scope<Hbox>(); }); }); }); };
|
||||
|
||||
auto view_cpu_index = [&] (auto &s, unsigned index) {
|
||||
s.template sub_scope<Vbox>([&] (auto &s) {
|
||||
s.template sub_scope<Annotation>(String<8>(index));
|
||||
view_cell_hspacer(s); }); };
|
||||
|
||||
auto view_leftaligned = [] (auto &s, auto text) {
|
||||
s.template sub_scope<Float>([&] (auto &s) {
|
||||
s.attribute("west", "yes");
|
||||
s.template sub_scope<Annotation>(text); }); };
|
||||
|
||||
auto selected = [] (Affinity::Location location, unsigned x, unsigned y)
|
||||
{
|
||||
return (unsigned(location.xpos()) <= x)
|
||||
&& (x < location.xpos() + location.width())
|
||||
&& (unsigned(location.ypos()) <= y)
|
||||
&& (y < location.ypos() + location.height());
|
||||
};
|
||||
|
||||
bool const have_hyperthreads = (space.height() > 1);
|
||||
|
||||
s.sub_scope<Hbox>([&] (Scope<Vbox, Hbox> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Hbox, Vbox> &s) {
|
||||
|
||||
for (unsigned y = 0; y < space.height(); y++) {
|
||||
s.sub_scope<Hbox>(Id { { y } }, [&] (Scope<Vbox, Hbox, Vbox, Hbox> &s) {
|
||||
for (unsigned x = 0; x < space.width(); x++)
|
||||
view_cell_cpu(s, Id { Id::Value(x) }, selected(location, x, y));
|
||||
|
||||
if (have_hyperthreads)
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Hbox, Vbox, Hbox, Float> &s) {
|
||||
view_hyperthread_index(s, y ); });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (have_hyperthreads)
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Hbox, Float> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Hbox, Float, Vbox> &s) {
|
||||
view_leftaligned(s, "Hyper");
|
||||
view_leftaligned(s, "threads"); }); });
|
||||
});
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Float> &s) {
|
||||
s.attribute("west", "yes");
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Float, Vbox> &s) {
|
||||
s.sub_scope<Hbox>([&] (Scope<Vbox, Float, Vbox, Hbox> &s) {
|
||||
for (unsigned x = 0; x < space.width(); x++)
|
||||
view_cpu_index(s, x);
|
||||
});
|
||||
s.sub_scope<Annotation>("Cores");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Resource_widget::Affinity_selector::click(Clicked_at const &at,
|
||||
Affinity::Space const &space,
|
||||
Affinity::Location &location)
|
||||
{
|
||||
Id const x_id = at.matching_id<Vbox, Hbox, Vbox, Hbox, Vbox>(),
|
||||
y_id = at.matching_id<Vbox, Hbox, Vbox, Hbox>();
|
||||
|
||||
unsigned x = ~0, y = ~0;
|
||||
|
||||
ascii_to(x_id.value.string(), x);
|
||||
ascii_to(y_id.value.string(), y);
|
||||
|
||||
if (x >= space.width() || y >= space.height())
|
||||
return;
|
||||
|
||||
unsigned loc_x = location.xpos();
|
||||
unsigned loc_y = location.ypos();
|
||||
unsigned loc_w = location.width();
|
||||
unsigned loc_h = location.height();
|
||||
|
||||
bool handled_x = false;
|
||||
bool handled_y = false;
|
||||
|
||||
if (x < loc_x) {
|
||||
loc_w += loc_x - x;
|
||||
loc_x = x;
|
||||
handled_x = true;
|
||||
} else if (x >= loc_x + loc_w) {
|
||||
loc_w = x - loc_x + 1;
|
||||
handled_x = true;
|
||||
}
|
||||
|
||||
if (y < loc_y) {
|
||||
loc_h += loc_y - y;
|
||||
loc_y = y;
|
||||
handled_y = true;
|
||||
} else if (y >= loc_y + loc_h) {
|
||||
loc_h = y - loc_y + 1;
|
||||
handled_y = true;
|
||||
}
|
||||
|
||||
if (handled_x && !handled_y) {
|
||||
handled_y = true;
|
||||
} else
|
||||
if (handled_y && !handled_x) {
|
||||
handled_x = true;
|
||||
}
|
||||
|
||||
if (!handled_x) {
|
||||
if ((x - loc_x) < (loc_x + loc_w - x)) {
|
||||
if (x - loc_x + 1 < loc_w) {
|
||||
loc_w -= x - loc_x + 1;
|
||||
loc_x = x + 1;
|
||||
} else {
|
||||
loc_w = loc_x + loc_w - x;
|
||||
loc_x = x;
|
||||
}
|
||||
} else {
|
||||
loc_w = x - loc_x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled_y) {
|
||||
if ((y - loc_y) < (loc_y + loc_h - y)) {
|
||||
if (y - loc_y + 1 < loc_h) {
|
||||
loc_h -= y - loc_y + 1;
|
||||
loc_y = y + 1;
|
||||
} else {
|
||||
loc_h = loc_y + loc_h - y;
|
||||
loc_y = y;
|
||||
}
|
||||
} else {
|
||||
loc_h = y - loc_y;
|
||||
}
|
||||
}
|
||||
|
||||
location = Affinity::Location(loc_x, loc_y, loc_w, loc_h);
|
||||
}
|
123
repos/gems/src/app/sculpt_manager/view/resource_widget.h
Normal file
123
repos/gems/src/app/sculpt_manager/view/resource_widget.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* \brief Resource assignment widget
|
||||
* \author Norman Feske
|
||||
* \date 2023-11-01
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__RESOURCE_WIDGET_H_
|
||||
#define _VIEW__RESOURCE_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include <model/component.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Resource_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Resource_widget : Widget<Vbox>
|
||||
{
|
||||
template <typename WIDGET>
|
||||
struct Titled_widget : Widget<Left_floating_hbox>
|
||||
{
|
||||
Hosted<Left_floating_hbox, Vbox, WIDGET> _hosted;
|
||||
|
||||
Titled_widget(auto &&... args) : _hosted(Id { "hosted" }, args...) { }
|
||||
|
||||
void view(Scope<Left_floating_hbox> &s, auto const &text, auto &&... args) const
|
||||
{
|
||||
/* title */
|
||||
s.sub_scope<Vbox>([&] (Scope<Left_floating_hbox, Vbox> &s) {
|
||||
s.sub_scope<Top_left_floating_hbox>([&] (auto &s) {
|
||||
|
||||
/*
|
||||
* The button is used to vertically align the "Priority"
|
||||
* label with the text of the first radio button.
|
||||
* The leading space is used to horizontally align
|
||||
* the label with the "Resource assignment ..." dialog
|
||||
* title.
|
||||
*/
|
||||
s.template sub_scope<Button>([&] (auto &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_node("hbox", [&] () { }); });
|
||||
|
||||
s.template sub_scope<Label>(String<32>(" ", text));
|
||||
});
|
||||
s.sub_scope<Min_ex>(11);
|
||||
});
|
||||
|
||||
s.sub_scope<Vbox>([&] (Scope<Left_floating_hbox, Vbox> &s) {
|
||||
s.widget(_hosted, args...); });
|
||||
|
||||
s.sub_scope<Hbox>();
|
||||
}
|
||||
|
||||
void click(auto &&... args) { _hosted.propagate(args...); }
|
||||
};
|
||||
|
||||
struct Priority_selector : Widget<Vbox>
|
||||
{
|
||||
Hosted<Vbox, Radio_select_button<Priority>>
|
||||
_buttons[4] { { Id { "Driver" }, Priority::DRIVER },
|
||||
{ Id { "Multimedia" }, Priority::MULTIMEDIA },
|
||||
{ Id { "Default" }, Priority::DEFAULT },
|
||||
{ Id { "Background" }, Priority::BACKGROUND } };
|
||||
|
||||
void view(Scope<Vbox> &s, Priority const &priority) const
|
||||
{
|
||||
for (auto const &button : _buttons)
|
||||
s.widget(button, priority);
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Priority &priority)
|
||||
{
|
||||
for (auto &button : _buttons)
|
||||
button.propagate(at, [&] (Priority value) { priority = value; });
|
||||
}
|
||||
};
|
||||
|
||||
struct Affinity_selector : Widget<Vbox>
|
||||
{
|
||||
void view(Scope<Vbox> &, Affinity::Space const &, Affinity::Location const &) const;
|
||||
|
||||
void click(Clicked_at const &, Affinity::Space const &, Affinity::Location &);
|
||||
};
|
||||
|
||||
Hosted<Vbox, Titled_widget<Affinity_selector>> _affinity { Id { "affinity" } };
|
||||
Hosted<Vbox, Titled_widget<Priority_selector>> _priority { Id { "priority" } };
|
||||
Hosted<Vbox, Menu_entry> _system { Id { "system control" } };
|
||||
|
||||
void view(Scope<Vbox> &s, Component const &component) const
|
||||
{
|
||||
s.sub_scope<Small_vgap>();
|
||||
|
||||
if (component.affinity_space.total() > 1) {
|
||||
s.widget(_affinity, "Affinity", component.affinity_space, component.affinity_location);
|
||||
s.sub_scope<Small_vgap>();
|
||||
}
|
||||
|
||||
s.widget(_priority, "Priority", component.priority);
|
||||
s.sub_scope<Small_vgap>();
|
||||
|
||||
s.widget(_system, component.system_control, "System control", "checkbox");
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Component &component)
|
||||
{
|
||||
if (component.affinity_space.total() > 1)
|
||||
_affinity.propagate(at, component.affinity_space, component.affinity_location);
|
||||
|
||||
_priority.propagate(at, component.priority),
|
||||
|
||||
_system.propagate(at, [&] {
|
||||
component.system_control = !component.system_control; });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__RESOURCE_WIDGET_H_ */
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* \brief GUI element that has a hovered and selected state
|
||||
* \author Norman Feske
|
||||
* \date 2018-05-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SELECTABLE_ITEM_H_
|
||||
#define _VIEW__SELECTABLE_ITEM_H_
|
||||
|
||||
#include "hoverable_item.h"
|
||||
|
||||
namespace Sculpt { struct Selectable_item; }
|
||||
|
||||
|
||||
struct Sculpt::Selectable_item : Hoverable_item
|
||||
{
|
||||
typedef Hoverable_item::Id Id;
|
||||
|
||||
Id _selected { };
|
||||
|
||||
/**
|
||||
* Apply click - if item is hovered, the click toggles the selection
|
||||
*/
|
||||
void toggle_selection_on_click()
|
||||
{
|
||||
if (_hovered.valid())
|
||||
_selected = (_hovered == _selected) ? Id() : _hovered;
|
||||
}
|
||||
|
||||
void select(Id const &id) { _selected = id; }
|
||||
|
||||
void reset() { _selected = Id{}; }
|
||||
|
||||
/**
|
||||
* Return true if item is currently selected
|
||||
*/
|
||||
bool selected(Id const &id) const { return id == _selected; }
|
||||
|
||||
bool any_selected() const { return _selected.valid(); }
|
||||
|
||||
/**
|
||||
* Generate button attributes depending on the item state
|
||||
*/
|
||||
void gen_button_attr(Xml_generator &xml, Id const &id) const
|
||||
{
|
||||
Hoverable_item::gen_button_attr(xml, id);
|
||||
|
||||
if (selected(id)) xml.attribute("selected", "yes");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SELECTABLE_ITEM_H_ */
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* \brief Settings dialog
|
||||
* \author Norman Feske
|
||||
* \date 2020-01-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SETTINGS_DIALOG_H_
|
||||
#define _VIEW__SETTINGS_DIALOG_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
#include <view/radio_choice_dialog.h>
|
||||
#include <model/settings.h>
|
||||
|
||||
namespace Sculpt { struct Settings_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Settings_dialog : Noncopyable, Deprecated_dialog
|
||||
{
|
||||
Settings const &_settings;
|
||||
|
||||
Hoverable_item _section { };
|
||||
|
||||
Radio_choice_dialog::Min_ex const _ratio { .left = 10, .right = 24 };
|
||||
|
||||
Radio_choice_dialog _font_size_choice { "Font size", _ratio };
|
||||
Radio_choice_dialog _keyboard_layout_choice { "Keyboard", _ratio };
|
||||
|
||||
static Radio_choice_dialog::Id _font_size_id(Settings::Font_size font_size)
|
||||
{
|
||||
switch (font_size) {
|
||||
case Settings::Font_size::SMALL: return "Small";
|
||||
case Settings::Font_size::MEDIUM: return "Medium";
|
||||
case Settings::Font_size::LARGE: return "Large";
|
||||
}
|
||||
return Radio_choice_dialog::Id();
|
||||
}
|
||||
|
||||
static Settings::Font_size _font_size(Radio_choice_dialog::Id id)
|
||||
{
|
||||
if (id == "Small") return Settings::Font_size::SMALL;
|
||||
if (id == "Medium") return Settings::Font_size::MEDIUM;
|
||||
if (id == "Large") return Settings::Font_size::LARGE;
|
||||
|
||||
return Settings::Font_size::MEDIUM;
|
||||
}
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
{
|
||||
return any_hover_changed(
|
||||
_section.match(hover, "frame", "vbox", "hbox", "name"),
|
||||
_font_size_choice.match_sub_dialog(hover, "frame", "vbox"),
|
||||
_keyboard_layout_choice.match_sub_dialog(hover, "frame", "vbox"));
|
||||
}
|
||||
|
||||
void reset() override { }
|
||||
|
||||
struct Action : Interface, Noncopyable
|
||||
{
|
||||
virtual void select_font_size(Settings::Font_size) = 0;
|
||||
|
||||
virtual void select_keyboard_layout(Settings::Keyboard_layout::Name const &) = 0;
|
||||
};
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
{
|
||||
gen_named_node(xml, "frame", "settings", [&] () {
|
||||
xml.node("vbox", [&] () {
|
||||
|
||||
using Choice = Radio_choice_dialog::Choice;
|
||||
|
||||
if (!_settings.manual_fonts_config) {
|
||||
_font_size_choice.generate(xml, _font_size_id(_settings.font_size),
|
||||
[&] (Choice const &choice) {
|
||||
choice.generate("Small");
|
||||
choice.generate("Medium");
|
||||
choice.generate("Large");
|
||||
});
|
||||
}
|
||||
|
||||
if (!_settings.manual_event_filter_config) {
|
||||
_keyboard_layout_choice.generate(xml, _settings.keyboard_layout,
|
||||
[&] (Choice const &choice) {
|
||||
using Keyboard_layout = Settings::Keyboard_layout;
|
||||
Keyboard_layout::for_each([&] (Keyboard_layout const &layout) {
|
||||
choice.generate(layout.name); });
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Click_result click(Action &action)
|
||||
{
|
||||
_font_size_choice.reset();
|
||||
_keyboard_layout_choice.reset();
|
||||
|
||||
Click_result result = Click_result::IGNORED;
|
||||
|
||||
auto handle_section = [&] (Radio_choice_dialog &dialog, auto fn_clicked)
|
||||
{
|
||||
if (result == Click_result::CONSUMED || !_section.hovered(dialog._id))
|
||||
return;
|
||||
|
||||
/* unfold radio choice */
|
||||
dialog.click();
|
||||
|
||||
auto selection = dialog.hovered_choice();
|
||||
if (selection == "")
|
||||
return;
|
||||
|
||||
fn_clicked(selection);
|
||||
|
||||
result = Click_result::CONSUMED;
|
||||
};
|
||||
|
||||
handle_section(_font_size_choice, [&] (auto selection) {
|
||||
action.select_font_size(_font_size(selection)); });
|
||||
|
||||
handle_section(_keyboard_layout_choice, [&] (auto selection) {
|
||||
using Keyboard_layout = Settings::Keyboard_layout;
|
||||
Keyboard_layout::for_each([&] (Keyboard_layout const &layout) {
|
||||
if (selection == layout.name)
|
||||
action.select_keyboard_layout(selection); }); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Settings_dialog(Settings const &settings) : _settings(settings) { }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SETTINGS_DIALOG_H_ */
|
122
repos/gems/src/app/sculpt_manager/view/settings_widget.h
Normal file
122
repos/gems/src/app/sculpt_manager/view/settings_widget.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* \brief Settings widget
|
||||
* \author Norman Feske
|
||||
* \date 2020-01-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020-2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SETTINGS_WIDGET_H_
|
||||
#define _VIEW__SETTINGS_WIDGET_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
#include <model/settings.h>
|
||||
|
||||
namespace Sculpt { struct Settings_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Settings_widget : Widget<Vbox>
|
||||
{
|
||||
Settings const &_settings;
|
||||
|
||||
enum class Selected_section { NONE, FONT_SIZE, KEYBAORD };
|
||||
|
||||
Selected_section _selected_section = Selected_section::NONE;
|
||||
|
||||
using Font_size = Settings::Font_size;
|
||||
using Keyboard_layout = Settings::Keyboard_layout;
|
||||
|
||||
static Id _font_size_id(Font_size font_size)
|
||||
{
|
||||
switch (font_size) {
|
||||
case Font_size::SMALL: return { "Small" };
|
||||
case Font_size::MEDIUM: return { "Medium" };
|
||||
case Font_size::LARGE: return { "Large" };
|
||||
}
|
||||
return { };
|
||||
}
|
||||
|
||||
struct Font_size_radio : Hosted<Radio_select_button<Font_size>>
|
||||
{
|
||||
Font_size_radio(Font_size s)
|
||||
: Hosted<Radio_select_button<Font_size>>(_font_size_id(s), s) { };
|
||||
};
|
||||
|
||||
Font_size_radio const _font_size_items[3] {
|
||||
Font_size::SMALL, Font_size::MEDIUM, Font_size::LARGE };
|
||||
|
||||
using Keyboard_radio = Hosted<Radio_select_button<Keyboard_layout::Name>>;
|
||||
|
||||
using Hosted_choice = Hosted<Vbox, Choice<Selected_section>>;
|
||||
|
||||
Hosted_choice const
|
||||
_font_size_choice { Id { "Font size" }, Selected_section::FONT_SIZE },
|
||||
_keyboard_layout_choice { Id { "Keyboard" }, Selected_section::KEYBAORD };
|
||||
|
||||
Settings_widget(Settings const &settings) : _settings(settings) { }
|
||||
|
||||
void view(Scope<Vbox> &s) const
|
||||
{
|
||||
unsigned const left_ex = 10, right_ex = 24;
|
||||
|
||||
if (!_settings.manual_fonts_config) {
|
||||
Font_size const selected = _settings.font_size;
|
||||
s.widget(_font_size_choice,
|
||||
Hosted_choice::Attr {
|
||||
.left_ex = left_ex, .right_ex = right_ex,
|
||||
.unfolded = _selected_section,
|
||||
.selected_item = _font_size_id(selected)
|
||||
},
|
||||
[&] (Hosted_choice::Sub_scope &s) {
|
||||
for (auto const &item : _font_size_items)
|
||||
s.widget(item, selected);
|
||||
});
|
||||
}
|
||||
|
||||
if (!_settings.manual_event_filter_config) {
|
||||
s.widget(_keyboard_layout_choice,
|
||||
Hosted_choice::Attr {
|
||||
.left_ex = left_ex, .right_ex = right_ex,
|
||||
.unfolded = _selected_section,
|
||||
.selected_item = _settings.keyboard_layout
|
||||
},
|
||||
[&] (Hosted_choice::Sub_scope &s) {
|
||||
Keyboard_layout::for_each([&] (Keyboard_layout const &layout) {
|
||||
s.widget(Keyboard_radio { Id { layout.name }, layout.name },
|
||||
_settings.keyboard_layout);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Action : Interface, Noncopyable
|
||||
{
|
||||
virtual void select_font_size(Font_size) = 0;
|
||||
virtual void select_keyboard_layout(Keyboard_layout::Name const &) = 0;
|
||||
};
|
||||
|
||||
void click(Clicked_at const &at, Action &action)
|
||||
{
|
||||
_font_size_choice.propagate(at, _selected_section,
|
||||
[&] { _selected_section = Selected_section::NONE; },
|
||||
[&] (Clicked_at const &at) {
|
||||
for (auto &item : _font_size_items)
|
||||
item.propagate(at, [&] (Font_size s) {
|
||||
action.select_font_size(s); });
|
||||
});
|
||||
|
||||
_keyboard_layout_choice.propagate(at, _selected_section,
|
||||
[&] { _selected_section = Selected_section::NONE; },
|
||||
[&] (Clicked_at const &at) {
|
||||
Id const id = at.matching_id<Keyboard_radio>();
|
||||
action.select_keyboard_layout(id.value);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SETTINGS_WIDGET_H_ */
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* \brief Dialog for the deploy presets
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_PRESETS_DIALOG_H_
|
||||
#define _VIEW__SOFTWARE_PRESETS_DIALOG_H_
|
||||
|
||||
#include <model/presets.h>
|
||||
#include <view/dialog.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Sculpt { struct Software_presets_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Software_presets_dialog : Widget<Float>
|
||||
{
|
||||
Presets const &_presets;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void load_deploy_preset(Presets::Info::Name const &) = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
|
||||
using Hover_result = Hoverable_item::Hover_result;
|
||||
|
||||
Presets::Info::Name _selected { };
|
||||
|
||||
Hoverable_item _item { };
|
||||
Activatable_item _operation { };
|
||||
|
||||
Software_presets_dialog(Presets const &presets, Action &action)
|
||||
:
|
||||
_presets(presets), _action(action)
|
||||
{ }
|
||||
|
||||
using Name = Presets::Info::Name;
|
||||
|
||||
void _gen_horizontal_spacer(Xml_generator &xml) const
|
||||
{
|
||||
gen_named_node(xml, "label", "spacer", [&] {
|
||||
xml.attribute("min_ex", 35); });
|
||||
}
|
||||
|
||||
void _gen_preset(Xml_generator &xml, Presets::Info const &preset) const
|
||||
{
|
||||
gen_named_node(xml, "vbox", preset.name, [&] () {
|
||||
|
||||
gen_named_node(xml, "hbox", preset.name, [&] () {
|
||||
|
||||
gen_named_node(xml, "float", "left", [&] () {
|
||||
xml.attribute("west", "yes");
|
||||
|
||||
xml.node("hbox", [&] () {
|
||||
gen_named_node(xml, "float", "radio", [&] () {
|
||||
gen_named_node(xml, "button", "button", [&] () {
|
||||
|
||||
_item.gen_hovered_attr(xml, preset.name);
|
||||
|
||||
if (_selected == preset.name)
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.attribute("style", "radio");
|
||||
|
||||
xml.node("hbox", [&] () { });
|
||||
});
|
||||
});
|
||||
|
||||
gen_named_node(xml, "label", "name", [&] () {
|
||||
xml.attribute("text", Name(" ", Pretty(preset.name))); });
|
||||
|
||||
gen_item_vspace(xml, "vspace");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (_selected != preset.name)
|
||||
return;
|
||||
|
||||
auto vspacer = [&] (auto name)
|
||||
{
|
||||
gen_named_node(xml, "label", name, [&] () {
|
||||
xml.attribute("text", " "); });
|
||||
};
|
||||
|
||||
vspacer("spacer1");
|
||||
|
||||
gen_named_node(xml, "float", "info", [&] () {
|
||||
gen_named_node(xml, "label", "text", [&] () {
|
||||
xml.attribute("text", preset.text); }); });
|
||||
|
||||
vspacer("spacer2");
|
||||
|
||||
gen_named_node(xml, "float", "operations", [&] () {
|
||||
gen_named_node(xml, "button", "load", [&] () {
|
||||
_operation.gen_button_attr(xml, "load");
|
||||
gen_named_node(xml, "label", "text", [&] () {
|
||||
xml.attribute("text", " Load "); });
|
||||
});
|
||||
});
|
||||
|
||||
vspacer("spacer3");
|
||||
});
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const
|
||||
{
|
||||
if (_presets.available())
|
||||
gen_named_node(xml, "float", "presets", [&] {
|
||||
xml.node("frame", [&] {
|
||||
xml.node("vbox", [&] {
|
||||
_gen_horizontal_spacer(xml);
|
||||
_presets.for_each([&] (Presets::Info const &info) {
|
||||
_gen_preset(xml, info); }); }); }); });
|
||||
}
|
||||
|
||||
Hover_result hover(Xml_node hover)
|
||||
{
|
||||
return Deprecated_dialog::any_hover_changed(
|
||||
_item.match (hover, "float", "frame", "vbox", "vbox", "hbox", "name"),
|
||||
_operation.match(hover, "float", "frame", "vbox", "vbox", "float", "button", "name")
|
||||
);
|
||||
}
|
||||
|
||||
bool hovered() const { return _item._hovered.valid(); }
|
||||
|
||||
void click()
|
||||
{
|
||||
if (_item._hovered.length() > 1)
|
||||
_selected = _item._hovered;
|
||||
|
||||
if (_operation.hovered("load"))
|
||||
_operation.propose_activation_on_click();
|
||||
}
|
||||
|
||||
void clack()
|
||||
{
|
||||
if (_selected.length() <= 1)
|
||||
return;
|
||||
|
||||
_operation.confirm_activation_on_clack();
|
||||
|
||||
if (_operation.activated("load")) {
|
||||
_action.load_deploy_preset(_selected);
|
||||
_selected = { };
|
||||
}
|
||||
|
||||
_operation.reset();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_PRESETS_DIALOG_H_ */
|
116
repos/gems/src/app/sculpt_manager/view/software_presets_widget.h
Normal file
116
repos/gems/src/app/sculpt_manager/view/software_presets_widget.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* \brief Widget for the deploy presets
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_PRESETS_WIDGET_H_
|
||||
#define _VIEW__SOFTWARE_PRESETS_WIDGET_H_
|
||||
|
||||
#include <model/presets.h>
|
||||
#include <view/dialog.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Sculpt { struct Software_presets_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Software_presets_widget : Widget<Float>
|
||||
{
|
||||
using Name = Presets::Info::Name;
|
||||
|
||||
Name _selected { };
|
||||
|
||||
struct Preset : Widget<Vbox>
|
||||
{
|
||||
using Radio = Hosted<Vbox, Radio_select_button<Name>>;
|
||||
using Load = Hosted<Vbox, Float, Deferred_action_button>;
|
||||
|
||||
void view(Scope<Vbox> &s, Presets::Info const &preset, Load const &load,
|
||||
Name const &selected) const
|
||||
{
|
||||
s.widget(Radio { Id { preset.name }, preset.name },
|
||||
selected, Name { " ", Pretty(preset.name) });
|
||||
|
||||
if (selected != preset.name)
|
||||
return;
|
||||
|
||||
s.sub_scope<Vgap>();
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Float> &s) {
|
||||
s.sub_scope<Label>(preset.text); });
|
||||
|
||||
s.sub_scope<Vgap>();
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Float> &s) {
|
||||
s.widget(load); });
|
||||
|
||||
s.sub_scope<Vgap>();
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Load &load) { load.propagate(at); }
|
||||
|
||||
void clack(Clacked_at const &at, Load &load, auto const &fn)
|
||||
{
|
||||
load.propagate(at, fn);
|
||||
}
|
||||
};
|
||||
|
||||
/* use one button to preserve 'Deferred_action_button' state across presets */
|
||||
Preset::Load _load { Id { " Load " } };
|
||||
|
||||
using Hosted_preset = Hosted<Float, Frame, Vbox, Preset>;
|
||||
|
||||
void view(Scope<Float> &s, Presets const &presets) const
|
||||
{
|
||||
s.sub_scope<Frame>([&] (Scope<Float, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Float, Frame, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(35);
|
||||
presets.for_each([&] (Presets::Info const &info) {
|
||||
Hosted_preset const hosted { Id { info.name } };
|
||||
s.widget(hosted, info, _load, _selected); }); }); });
|
||||
}
|
||||
|
||||
void _with_selected_preset(Presets const &presets, auto const &fn)
|
||||
{
|
||||
presets.for_each([&] (Presets::Info const &info) {
|
||||
if (info.name == _selected)
|
||||
fn(info); });
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Presets const &presets)
|
||||
{
|
||||
/* unfold preset info */
|
||||
Id const id = at.matching_id<Float, Frame, Vbox, Vbox>();
|
||||
if (id.valid())
|
||||
_selected = id.value;
|
||||
|
||||
_with_selected_preset(presets, [&] (Presets::Info const &info) {
|
||||
Hosted_preset hosted { Id { info.name } };
|
||||
hosted.propagate(at, _load); });
|
||||
}
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void load_deploy_preset(Presets::Info::Name const &) = 0;
|
||||
};
|
||||
|
||||
void clack(Clacked_at const &at, Presets const &presets, Action &action)
|
||||
{
|
||||
_with_selected_preset(presets, [&] (Presets::Info const &info) {
|
||||
Hosted_preset hosted { Id { info.name } };
|
||||
hosted.propagate(at, _load, [&] {
|
||||
action.load_deploy_preset(_selected);
|
||||
_selected = { };
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_PRESETS_WIDGET_H_ */
|
@ -1,369 +0,0 @@
|
||||
/*
|
||||
* \brief Dialog for software update
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_UPDATE_DIALOG_H_
|
||||
#define _VIEW__SOFTWARE_UPDATE_DIALOG_H_
|
||||
|
||||
#include <model/nic_state.h>
|
||||
#include <model/build_info.h>
|
||||
#include <model/download_queue.h>
|
||||
#include <model/index_update_queue.h>
|
||||
#include <view/depot_users_dialog.h>
|
||||
|
||||
namespace Sculpt { struct Software_update_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Software_update_dialog : Widget<Vbox>
|
||||
{
|
||||
using Depot_users = Depot_users_dialog::Depot_users;
|
||||
using User = Depot_users_dialog::User;
|
||||
using Image_index = Attached_rom_dataspace;
|
||||
using Url = Depot_users_dialog::Url;
|
||||
using Version = String<16>;
|
||||
using User_properties = Depot_users_dialog::User_properties;
|
||||
|
||||
Build_info const _build_info;
|
||||
|
||||
Nic_state const &_nic_state;
|
||||
Download_queue const &_download_queue;
|
||||
Index_update_queue const &_index_update_queue;
|
||||
File_operation_queue const &_file_operation_queue;
|
||||
Image_index const &_image_index;
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void query_image_index (User const &) = 0;
|
||||
virtual void trigger_image_download(Path const &, Verify) = 0;
|
||||
virtual void update_image_index (User const &, Verify) = 0;
|
||||
virtual void install_boot_image (Path const &) = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
|
||||
Depot_users_dialog _users;
|
||||
|
||||
Path _last_installed { };
|
||||
Path _last_selected { };
|
||||
|
||||
Path _index_path() const { return Path(_users.selected(), "/image/index"); }
|
||||
|
||||
bool _index_update_in_progress() const
|
||||
{
|
||||
using Update = Index_update_queue::Update;
|
||||
|
||||
bool result = false;
|
||||
_index_update_queue.with_update(_index_path(), [&] (Update const &update) {
|
||||
if (update.active())
|
||||
result = true; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Path _image_path(Version const &version) const
|
||||
{
|
||||
return Path(_users.selected(), "/image/sculpt-", _build_info.board, "-", version);
|
||||
}
|
||||
|
||||
bool _installing() const
|
||||
{
|
||||
return _file_operation_queue.copying_to_path("/rw/boot");
|
||||
};
|
||||
|
||||
Hoverable_item _check { };
|
||||
Hoverable_item _version { };
|
||||
Hoverable_item _operation { };
|
||||
|
||||
using Hover_result = Hoverable_item::Hover_result;
|
||||
|
||||
Software_update_dialog(Build_info const &build_info,
|
||||
Nic_state const &nic_state,
|
||||
Download_queue const &download_queue,
|
||||
Index_update_queue const &index_update_queue,
|
||||
File_operation_queue const &file_operation_queue,
|
||||
Depot_users const &depot_users,
|
||||
Image_index const &image_index,
|
||||
Depot_users_dialog::Action &depot_users_action,
|
||||
Action &action)
|
||||
:
|
||||
_build_info(build_info), _nic_state(nic_state),
|
||||
_download_queue(download_queue),
|
||||
_index_update_queue(index_update_queue),
|
||||
_file_operation_queue(file_operation_queue),
|
||||
_image_index(image_index),
|
||||
_action(action),
|
||||
_users(depot_users, _build_info.depot_user, depot_users_action)
|
||||
{ }
|
||||
|
||||
static void _gen_vspacer(Xml_generator &xml, char const *name)
|
||||
{
|
||||
gen_named_node(xml, "label", name, [&] () {
|
||||
xml.attribute("text", " ");
|
||||
xml.attribute("font", "annotation/regular");
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_image_main(Xml_generator &xml, Xml_node const &image) const
|
||||
{
|
||||
Version const version = image.attribute_value("version", Version());
|
||||
bool const present = image.attribute_value("present", false);
|
||||
Path const path = _image_path(version);
|
||||
|
||||
struct Download_state
|
||||
{
|
||||
bool in_progress;
|
||||
bool failed;
|
||||
unsigned percent;
|
||||
};
|
||||
|
||||
using Download = Download_queue::Download;
|
||||
|
||||
auto state_from_download_queue = [&]
|
||||
{
|
||||
Download_state result { };
|
||||
_download_queue.with_download(path, [&] (Download const &download) {
|
||||
|
||||
if (download.state == Download::State::DOWNLOADING)
|
||||
result.in_progress = true;
|
||||
|
||||
if (download.state == Download::State::FAILED)
|
||||
result.failed = true;
|
||||
|
||||
result.percent = download.percent;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
Download_state const download_state = state_from_download_queue();
|
||||
|
||||
gen_named_node(xml, "float", "label", [&] {
|
||||
xml.attribute("west", "yes");
|
||||
gen_named_node(xml, "label", "label", [&] {
|
||||
xml.attribute("text", String<50>(" ", version));
|
||||
xml.attribute("min_ex", "15");
|
||||
});
|
||||
});
|
||||
|
||||
auto gen_status = [&] (auto message)
|
||||
{
|
||||
gen_named_node(xml, "float", "status", [&] {
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("font", "annotation/regular");
|
||||
xml.attribute("text", message); }); });
|
||||
};
|
||||
|
||||
if (image.has_sub_node("info")) {
|
||||
if (_last_selected == path)
|
||||
gen_status("Changes");
|
||||
else
|
||||
gen_status("...");
|
||||
}
|
||||
|
||||
if (download_state.in_progress && download_state.percent)
|
||||
gen_status(String<16>(download_state.percent, "%"));
|
||||
|
||||
if (download_state.failed)
|
||||
gen_status("unavailable");
|
||||
|
||||
if (_last_installed == path) {
|
||||
if (_installing())
|
||||
gen_status("installing...");
|
||||
else
|
||||
gen_status("reboot to activate");
|
||||
}
|
||||
|
||||
gen_named_node(xml, "float", "buttons", [&] {
|
||||
xml.attribute("east", "yes");
|
||||
xml.node("hbox", [&] {
|
||||
|
||||
auto gen_button = [&] (auto id, bool selected, auto text)
|
||||
{
|
||||
gen_named_node(xml, "button", id, [&] {
|
||||
|
||||
if (version == _version._hovered)
|
||||
_operation.gen_hovered_attr(xml, id);
|
||||
|
||||
if (selected) {
|
||||
xml.attribute("selected", "yes");
|
||||
xml.attribute("style", "unimportant");
|
||||
}
|
||||
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("text", text); });
|
||||
});
|
||||
};
|
||||
|
||||
if (present)
|
||||
gen_button("install", _installing(), " Install ");
|
||||
|
||||
if (!present)
|
||||
gen_button("download", download_state.in_progress, " Download ");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_image_info(Xml_generator &xml, Xml_node const &image) const
|
||||
{
|
||||
gen_named_node(xml, "vbox", "main", [&] {
|
||||
|
||||
unsigned line = 0;
|
||||
|
||||
image.for_each_sub_node("info", [&] (Xml_node const &info) {
|
||||
|
||||
/* limit changelog to a sensible maximum of lines */
|
||||
if (++line > 8)
|
||||
return;
|
||||
|
||||
using Text = String<80>;
|
||||
Text const text = info.attribute_value("text", Text());
|
||||
|
||||
gen_named_node(xml, "float", String<16>(line), [&] {
|
||||
xml.attribute("west", "yes");
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("text", text);
|
||||
xml.attribute("font", "annotation/regular");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_image_entry(Xml_generator &xml, Xml_node const &image) const
|
||||
{
|
||||
Version const version = image.attribute_value("version", Version());
|
||||
Path const path = _image_path(version);
|
||||
|
||||
gen_named_node(xml, "frame", version, [&] {
|
||||
xml.attribute("style", "important");
|
||||
|
||||
xml.node("vbox", [&] {
|
||||
|
||||
gen_named_node(xml, "float", "main", [&] {
|
||||
xml.attribute("east", "yes");
|
||||
xml.attribute("west", "yes");
|
||||
_gen_image_main(xml, image);
|
||||
});
|
||||
|
||||
if (path == _last_selected && image.has_sub_node("info")) {
|
||||
_gen_vspacer(xml, "above");
|
||||
gen_named_node(xml, "float", "info", [&] {
|
||||
_gen_image_info(xml, image); });
|
||||
_gen_vspacer(xml, "below");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _gen_image_list(Xml_generator &xml) const
|
||||
{
|
||||
Xml_node const index = _image_index.xml();
|
||||
|
||||
index.for_each_sub_node("user", [&] (Xml_node const &user) {
|
||||
if (user.attribute_value("name", User()) == _users.selected())
|
||||
user.for_each_sub_node("image", [&] (Xml_node const &image) {
|
||||
_gen_image_entry(xml, image); }); });
|
||||
}
|
||||
|
||||
void _gen_update_dialog(Xml_generator &xml) const
|
||||
{
|
||||
gen_named_node(xml, "frame", "update_dialog", [&] {
|
||||
xml.node("vbox", [&] {
|
||||
_users.generate(xml);
|
||||
|
||||
User_properties const properties = _users.selected_user_properties();
|
||||
|
||||
bool const offer_index_update = _users.one_selected()
|
||||
&& _nic_state.ready()
|
||||
&& properties.download_url;
|
||||
if (offer_index_update) {
|
||||
_gen_vspacer(xml, "above check");
|
||||
gen_named_node(xml, "float", "check", [&] {
|
||||
gen_named_node(xml, "button", "check", [&] {
|
||||
_check.gen_hovered_attr(xml, "check");
|
||||
if (_index_update_in_progress()) {
|
||||
xml.attribute("selected", "yes");
|
||||
xml.attribute("style", "unimportant");
|
||||
}
|
||||
xml.node("label", [&] {
|
||||
auto const text = properties.public_key
|
||||
? " Check for Updates "
|
||||
: " Check for unverified Updates ";
|
||||
xml.attribute("text", text); });
|
||||
});
|
||||
});
|
||||
_gen_vspacer(xml, "below check");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
_gen_image_list(xml);
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const
|
||||
{
|
||||
gen_named_node(xml, "vbox", "update", [&] {
|
||||
_gen_update_dialog(xml); });
|
||||
}
|
||||
|
||||
Hover_result hover(Xml_node const &hover)
|
||||
{
|
||||
_users.reset_hover();
|
||||
|
||||
return Deprecated_dialog::any_hover_changed(
|
||||
match_sub_dialog(hover, _users, "vbox", "frame", "vbox"),
|
||||
_check .match(hover, "vbox", "frame", "vbox", "float", "button", "name"),
|
||||
_version .match(hover, "vbox", "frame", "name"),
|
||||
_operation.match(hover, "vbox", "frame", "vbox", "float", "float", "hbox", "button", "name")
|
||||
);
|
||||
}
|
||||
|
||||
bool hovered() const { return _users.hovered(); }
|
||||
|
||||
void click()
|
||||
{
|
||||
Verify const verify { _users.selected_user_properties().public_key };
|
||||
|
||||
if (_users.hovered())
|
||||
_users.click([&] (User const &selected_user) {
|
||||
_action.query_image_index(selected_user); });
|
||||
|
||||
if (_check.hovered("check") && !_index_update_in_progress())
|
||||
_action.update_image_index(_users.selected(), verify);
|
||||
|
||||
if (_operation.hovered("download"))
|
||||
_action.trigger_image_download(_image_path(_version._hovered), verify);
|
||||
|
||||
if (_version._hovered.length() > 1)
|
||||
_last_selected = _image_path(_version._hovered);
|
||||
|
||||
if (_operation.hovered("install") && !_installing()) {
|
||||
_last_installed = _image_path(_version._hovered);
|
||||
_action.install_boot_image(_last_installed);
|
||||
}
|
||||
}
|
||||
|
||||
void clack() { }
|
||||
|
||||
bool keyboard_needed() const { return _users.keyboard_needed(); }
|
||||
|
||||
void handle_key(Codepoint c) { _users.handle_key(c); }
|
||||
|
||||
void sanitize_user_selection() { _users.sanitize_unfold_state(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
_last_installed = { };
|
||||
_last_selected = { };
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_UPDATE_DIALOG_H_ */
|
307
repos/gems/src/app/sculpt_manager/view/software_update_widget.h
Normal file
307
repos/gems/src/app/sculpt_manager/view/software_update_widget.h
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* \brief Widget for software update
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_UPDATE_WIDGET_H_
|
||||
#define _VIEW__SOFTWARE_UPDATE_WIDGET_H_
|
||||
|
||||
#include <model/nic_state.h>
|
||||
#include <model/build_info.h>
|
||||
#include <model/download_queue.h>
|
||||
#include <model/index_update_queue.h>
|
||||
#include <view/depot_users_widget.h>
|
||||
|
||||
namespace Sculpt { struct Software_update_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Software_update_widget : Widget<Vbox>
|
||||
{
|
||||
using Depot_users = Depot_users_widget::Depot_users;
|
||||
using User = Depot_users_widget::User;
|
||||
using Image_index = Attached_rom_dataspace;
|
||||
using Version = String<16>;
|
||||
|
||||
Build_info const _build_info;
|
||||
|
||||
Nic_state const &_nic_state;
|
||||
Download_queue const &_download_queue;
|
||||
Index_update_queue const &_index_update_queue;
|
||||
File_operation_queue const &_file_operation_queue;
|
||||
Image_index const &_image_index;
|
||||
|
||||
Path _last_installed { };
|
||||
Path _last_selected { };
|
||||
|
||||
Hosted<Vbox, Frame, Vbox, Depot_users_widget> _users;
|
||||
|
||||
Path _index_path() const { return Path(_users.selected(), "/image/index"); }
|
||||
|
||||
bool _index_update_in_progress() const
|
||||
{
|
||||
using Update = Index_update_queue::Update;
|
||||
|
||||
bool result = false;
|
||||
_index_update_queue.with_update(_index_path(), [&] (Update const &update) {
|
||||
if (update.active())
|
||||
result = true; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Path _image_path(Version const &version) const
|
||||
{
|
||||
return Path(_users.selected(), "/image/sculpt-", _build_info.board, "-", version);
|
||||
}
|
||||
|
||||
bool _installing() const
|
||||
{
|
||||
return _file_operation_queue.copying_to_path("/rw/boot");
|
||||
}
|
||||
|
||||
struct Download_state
|
||||
{
|
||||
bool in_progress;
|
||||
bool failed;
|
||||
unsigned percent;
|
||||
|
||||
static Download_state from_queue(Download_queue const &queue, Path const &path)
|
||||
{
|
||||
using Download = Download_queue::Download;
|
||||
|
||||
Download_state result { };
|
||||
queue.with_download(path, [&] (Download const &download) {
|
||||
result = {
|
||||
.in_progress = (download.state == Download::State::DOWNLOADING),
|
||||
.failed = (download.state == Download::State::FAILED),
|
||||
.percent = download.percent
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct Image_main : Widget<Float>
|
||||
{
|
||||
struct Attr
|
||||
{
|
||||
Version version;
|
||||
Path path;
|
||||
bool present;
|
||||
bool changelog;
|
||||
Download_state download_state;
|
||||
bool selected;
|
||||
bool last_installed;
|
||||
bool installing;
|
||||
};
|
||||
|
||||
Hosted<Float, Right_floating_hbox, Operation_button> const
|
||||
_install { Id { "Install" } },
|
||||
_download { Id { "Download" } };
|
||||
|
||||
void view(Scope<Float> &s, Attr const &attr) const
|
||||
{
|
||||
s.attribute("east", "yes");
|
||||
s.attribute("west", "yes");
|
||||
|
||||
s.sub_scope<Left_floating_text>(attr.version);
|
||||
|
||||
auto status_message = [] (Attr const &attr) -> String<50>
|
||||
{
|
||||
if (attr.last_installed)
|
||||
return { attr.installing ? "installing..." : "reboot to activate" };
|
||||
|
||||
if (attr.download_state.failed)
|
||||
return { "unavailable" };
|
||||
|
||||
if (attr.download_state.in_progress && attr.download_state.percent)
|
||||
return { attr.download_state.percent, "%" };
|
||||
|
||||
if (attr.changelog)
|
||||
return { attr.selected ? "Changes" : "..." };
|
||||
|
||||
return { };
|
||||
};
|
||||
|
||||
s.sub_scope<Float>([&] (Scope<Float, Float> &s) {
|
||||
s.sub_scope<Annotation>(status_message(attr)); });
|
||||
|
||||
s.sub_scope<Right_floating_hbox>([&] (Scope<Float, Right_floating_hbox> &s) {
|
||||
|
||||
if (attr.present)
|
||||
s.widget(_install, attr.installing);
|
||||
else
|
||||
s.widget(_download, attr.download_state.in_progress);
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at,
|
||||
auto const &install_fn, auto const &download_fn) const
|
||||
{
|
||||
_install .propagate(at, install_fn);
|
||||
_download.propagate(at, download_fn);
|
||||
}
|
||||
};
|
||||
|
||||
struct Changelog : Widget<Float>
|
||||
{
|
||||
void view(Scope<Float> &s, Xml_node const &image) const
|
||||
{
|
||||
using Text = String<80>;
|
||||
|
||||
unsigned line = 0; /* limit changelog to a sensible max. of lines */
|
||||
|
||||
s.sub_scope<Vbox>([&] (Scope<Float, Vbox> &s) {
|
||||
s.sub_scope<Small_vgap>();
|
||||
image.for_each_sub_node("info", [&] (Xml_node const &info) {
|
||||
if (++line < 9)
|
||||
s.sub_scope<Left_annotation>(info.attribute_value("text", Text()));
|
||||
});
|
||||
s.sub_scope<Small_vgap>();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* used accross images */
|
||||
Hosted<Vbox, Frame, Vbox, Image_main> const _hosted_image_main { Id { "main" } };
|
||||
|
||||
void _view_image_entry(Scope<Vbox> &s, Xml_node const &image) const
|
||||
{
|
||||
Version const version = image.attribute_value("version", Version());
|
||||
Path const path = _image_path(version);
|
||||
|
||||
Image_main::Attr const attr {
|
||||
.version = version,
|
||||
.path = path,
|
||||
.present = image.attribute_value("present", false),
|
||||
.changelog = image.has_sub_node("info"),
|
||||
.download_state = Download_state::from_queue(_download_queue, path),
|
||||
.selected = (_last_selected == path),
|
||||
.last_installed = (_last_installed == path),
|
||||
.installing = _installing(),
|
||||
};
|
||||
|
||||
s.sub_scope<Frame>(Id { version }, [&] (Scope<Vbox, Frame> &s) {
|
||||
s.attribute("style", "important");
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||
|
||||
s.widget(_hosted_image_main, attr);
|
||||
|
||||
if (path == _last_selected && attr.changelog)
|
||||
s.widget(Hosted<Vbox, Frame, Vbox, Changelog> { Id { "changes" } },
|
||||
image);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Hosted<Vbox, Frame, Vbox, Float, Operation_button> _check { Id { "check" } };
|
||||
|
||||
Software_update_widget(Build_info const &build_info,
|
||||
Nic_state const &nic_state,
|
||||
Download_queue const &download_queue,
|
||||
Index_update_queue const &index_update_queue,
|
||||
File_operation_queue const &file_operation_queue,
|
||||
Depot_users const &depot_users,
|
||||
Image_index const &image_index)
|
||||
:
|
||||
_build_info(build_info), _nic_state(nic_state),
|
||||
_download_queue(download_queue),
|
||||
_index_update_queue(index_update_queue),
|
||||
_file_operation_queue(file_operation_queue),
|
||||
_image_index(image_index),
|
||||
_users(Id { "users" }, depot_users, _build_info.depot_user)
|
||||
{ }
|
||||
|
||||
void view(Scope<Vbox> &s, Xml_node const &image_index) const
|
||||
{
|
||||
/* use empty ID to not interfere with matching the version in 'click'*/
|
||||
s.sub_scope<Frame>(Id { }, [&] (Scope<Vbox, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||
|
||||
s.widget(_users);
|
||||
|
||||
Depot_users_widget::User_properties const
|
||||
properties = _users.selected_user_properties();
|
||||
|
||||
bool const offer_index_update = _users.one_selected()
|
||||
&& _nic_state.ready()
|
||||
&& properties.download_url;
|
||||
if (!offer_index_update)
|
||||
return;
|
||||
|
||||
s.sub_scope<Small_vgap>();
|
||||
s.sub_scope<Float>([&] (Scope<Vbox, Frame, Vbox, Float> &s) {
|
||||
s.widget(_check, _index_update_in_progress(),
|
||||
properties.public_key ? "Check for Updates"
|
||||
: "Check for unverified Updates");
|
||||
});
|
||||
s.sub_scope<Small_vgap>();
|
||||
});
|
||||
});
|
||||
|
||||
image_index.for_each_sub_node("user", [&] (Xml_node const &user) {
|
||||
if (user.attribute_value("name", User()) == _users.selected())
|
||||
user.for_each_sub_node("image", [&] (Xml_node const &image) {
|
||||
_view_image_entry(s, image); }); });
|
||||
}
|
||||
|
||||
struct Action : virtual Depot_users_widget::Action
|
||||
{
|
||||
virtual void query_image_index (User const &) = 0;
|
||||
virtual void trigger_image_download(Path const &, Verify) = 0;
|
||||
virtual void update_image_index (User const &, Verify) = 0;
|
||||
virtual void install_boot_image (Path const &) = 0;
|
||||
};
|
||||
|
||||
void click(Clicked_at const &at, Action &action)
|
||||
{
|
||||
_users.propagate(at, action, [&] (User const &selected_user) {
|
||||
action.query_image_index(selected_user); });
|
||||
|
||||
Verify const verify { _users.selected_user_properties().public_key };
|
||||
|
||||
if (!_index_update_in_progress())
|
||||
_check.propagate(at, [&] {
|
||||
action.update_image_index(_users.selected(), verify); });
|
||||
|
||||
Id const version = at.matching_id<Vbox, Frame>();
|
||||
Path const path = _image_path(version.value);
|
||||
|
||||
if (version.valid()) {
|
||||
_last_selected = path;
|
||||
|
||||
_hosted_image_main.propagate(at,
|
||||
[&] /* install */ {
|
||||
if (!_installing()) {
|
||||
action.install_boot_image(path);
|
||||
_last_installed = path;
|
||||
}
|
||||
},
|
||||
[&] /* download */ {
|
||||
action.trigger_image_download(path, verify);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool keyboard_needed() const { return _users.keyboard_needed(); }
|
||||
|
||||
void handle_key(Codepoint c, Action &action) { _users.handle_key(c, action); }
|
||||
|
||||
void sanitize_user_selection() { _users.sanitize_unfold_state(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
_last_installed = { };
|
||||
_last_selected = { };
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_UPDATE_WIDGET_H_ */
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* \brief Dialog for showing the system version
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_VERSION_DIALOG_H_
|
||||
#define _VIEW__SOFTWARE_VERSION_DIALOG_H_
|
||||
|
||||
#include <model/build_info.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Software_version_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Software_version_dialog : Widget<Frame>
|
||||
{
|
||||
Build_info const _build_info;
|
||||
|
||||
Software_version_dialog(Build_info const &info) : _build_info(info) { }
|
||||
|
||||
void generate(Xml_generator &xml) const
|
||||
{
|
||||
using Version = Build_info::Version;
|
||||
auto padded = [] (Version const &v) { return Version(" ", v, " "); };
|
||||
|
||||
gen_named_node(xml, "frame", "version", [&] {
|
||||
xml.node("vbox", [&] {
|
||||
gen_named_node(xml, "label", "image", [&] {
|
||||
xml.attribute("text", padded(_build_info.image_version())); });
|
||||
gen_named_node(xml, "label", "genode", [&] {
|
||||
xml.attribute("text", padded(_build_info.genode_version()));
|
||||
xml.attribute("font", "annotation/regular");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_VERSION_DIALOG_H_ */
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* \brief Widget for showing the system version
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__SOFTWARE_VERSION_WIDGET_H_
|
||||
#define _VIEW__SOFTWARE_VERSION_WIDGET_H_
|
||||
|
||||
#include <model/build_info.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct Software_version_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Software_version_widget : Widget<Frame>
|
||||
{
|
||||
void view(Scope<Frame> &s, Build_info const &info) const
|
||||
{
|
||||
using Version = Build_info::Version;
|
||||
|
||||
auto padded = [] (Version const &v) { return Version(" ", v, " "); };
|
||||
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||
s.sub_scope<Label> (padded(info.image_version()));
|
||||
s.sub_scope<Annotation>(padded(info.genode_version()));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SOFTWARE_VERSION_WIDGET_H_ */
|
@ -1,21 +1,18 @@
|
||||
/*
|
||||
* \brief Storage-device management dialog
|
||||
* \brief Storage-device management widget
|
||||
* \author Norman Feske
|
||||
* \date 2018-04-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
* Copyright (C) 2018-2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
|
||||
/* local includes */
|
||||
#include "storage_device_dialog.h"
|
||||
#include <view/storage_device_widget.h>
|
||||
|
||||
using namespace Sculpt;
|
||||
|
||||
@ -27,8 +24,6 @@ struct Dialog::Partition_button : Widget<Hbox>
|
||||
void view(Scope<Hbox> &s, bool selected, Storage_target const &used_target,
|
||||
Storage_device const &device, Partition const &partition) const
|
||||
{
|
||||
using Label = Dialog::Label;
|
||||
|
||||
bool const hovered = s.hovered();
|
||||
|
||||
s.sub_scope<Left_floating_hbox>([&] (Scope<Hbox, Left_floating_hbox> &s) {
|
||||
@ -53,7 +48,7 @@ struct Dialog::Partition_button : Widget<Hbox>
|
||||
};
|
||||
|
||||
|
||||
void Storage_device_dialog::view(Scope<Vbox> &s, Storage_device const &dev,
|
||||
void Storage_device_widget::view(Scope<Vbox> &s, Storage_device const &dev,
|
||||
Storage_target const &used_target) const
|
||||
{
|
||||
dev.partitions.for_each([&] (Partition const &partition) {
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Storage-device management dialog
|
||||
* \brief Storage-device management widget
|
||||
* \author Norman Feske
|
||||
* \date 2020-01-29
|
||||
*/
|
||||
@ -11,16 +11,16 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__STORAGE_DEVICE_DIALOG_H_
|
||||
#define _VIEW__STORAGE_DEVICE_DIALOG_H_
|
||||
#ifndef _VIEW__STORAGE_DEVICE_WIDGET_H_
|
||||
#define _VIEW__STORAGE_DEVICE_WIDGET_H_
|
||||
|
||||
#include <types.h>
|
||||
#include <view/partition_operations.h>
|
||||
|
||||
namespace Sculpt { struct Storage_device_dialog; }
|
||||
namespace Sculpt { struct Storage_device_widget; }
|
||||
|
||||
|
||||
struct Sculpt::Storage_device_dialog : Widget<Vbox>
|
||||
struct Sculpt::Storage_device_widget : Widget<Vbox>
|
||||
{
|
||||
Partition::Number _selected_partition { };
|
||||
|
||||
@ -63,4 +63,4 @@ struct Sculpt::Storage_device_dialog : Widget<Vbox>
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__STORAGE_DEVICE_DIALOG_H_ */
|
||||
#endif /* _VIEW__STORAGE_DEVICE_WIDGET_H_ */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief Storage management dialog
|
||||
* \brief Storage management widget
|
||||
* \author Norman Feske
|
||||
* \date 2018-04-30
|
||||
*/
|
||||
@ -11,25 +11,26 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _VIEW__STORAGE_DIALOG_H_
|
||||
#define _VIEW__STORAGE_DIALOG_H_
|
||||
#ifndef _VIEW__STORAGE_WIDGET_H_
|
||||
#define _VIEW__STORAGE_WIDGET_H_
|
||||
|
||||
#include <types.h>
|
||||
#include <model/storage_devices.h>
|
||||
#include <view/storage_device_dialog.h>
|
||||
#include <view/storage_device_widget.h>
|
||||
|
||||
namespace Sculpt { struct Storage_devices_dialog_base; }
|
||||
namespace Sculpt { struct Storage_devices_widget_base; }
|
||||
|
||||
struct Sculpt::Storage_devices_dialog_base : Widget<Vbox>
|
||||
|
||||
struct Sculpt::Storage_devices_widget_base : Widget<Vbox>
|
||||
{
|
||||
Storage_devices const &_storage_devices;
|
||||
Storage_target const &_used_target;
|
||||
|
||||
Constructible<Hosted<Vbox, Frame, Storage_device_dialog>> _storage_device_dialog { };
|
||||
Constructible<Hosted<Vbox, Frame, Storage_device_widget>> _storage_device_widget { };
|
||||
|
||||
Block_device::Label _selected_device { };
|
||||
|
||||
Storage_devices_dialog_base(Storage_devices const &storage_devices,
|
||||
Storage_devices_widget_base(Storage_devices const &storage_devices,
|
||||
Storage_target const &used_target)
|
||||
:
|
||||
_storage_devices(storage_devices), _used_target(used_target)
|
||||
@ -42,48 +43,48 @@ struct Sculpt::Storage_devices_dialog_base : Widget<Vbox>
|
||||
|
||||
s.widget(button, dev, selected, _used_target);
|
||||
|
||||
if (_storage_device_dialog.constructed() && selected) {
|
||||
if (_storage_device_widget.constructed() && selected) {
|
||||
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
||||
s.attribute("style", "invisible");
|
||||
s.widget(*_storage_device_dialog, dev, _used_target);
|
||||
s.widget(*_storage_device_widget, dev, _used_target);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BUTTON>
|
||||
void _click_device(Clicked_at const &at, Storage_device_dialog::Action &action)
|
||||
void _click_device(Clicked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
/* select device */
|
||||
Id const id = at.matching_id<Vbox, BUTTON>();
|
||||
if (id.valid()) {
|
||||
if (id.value == _selected_device) {
|
||||
_selected_device = { };
|
||||
_storage_device_dialog.destruct();
|
||||
_storage_device_widget.destruct();
|
||||
} else {
|
||||
_selected_device = id.value;
|
||||
_storage_device_dialog.construct(Id { id });
|
||||
_storage_device_widget.construct(Id { id });
|
||||
}
|
||||
}
|
||||
|
||||
if (_selected_device.valid() && _storage_device_dialog.constructed())
|
||||
_storage_device_dialog->propagate(at, _used_target, action);
|
||||
if (_selected_device.valid() && _storage_device_widget.constructed())
|
||||
_storage_device_widget->propagate(at, _used_target, action);
|
||||
}
|
||||
|
||||
void _clack_device(Clacked_at const &at, Storage_device_dialog::Action &action)
|
||||
void _clack_device(Clacked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
if (_selected_device.valid() && _storage_device_dialog.constructed())
|
||||
_storage_device_dialog->propagate(at, action);
|
||||
if (_selected_device.valid() && _storage_device_widget.constructed())
|
||||
_storage_device_widget->propagate(at, action);
|
||||
}
|
||||
|
||||
void reset_operation()
|
||||
{
|
||||
if (_storage_device_dialog.constructed())
|
||||
_storage_device_dialog->reset_operation();
|
||||
if (_storage_device_widget.constructed())
|
||||
_storage_device_widget->reset_operation();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
_storage_device_dialog.destruct();
|
||||
_storage_device_widget.destruct();
|
||||
_selected_device = { };
|
||||
}
|
||||
};
|
||||
@ -99,8 +100,6 @@ struct Sculpt::Block_device_button : Widget<Button>
|
||||
if (s.hovered()) s.attribute("hovered", "yes");
|
||||
if (selected) s.attribute("selected", "yes");
|
||||
|
||||
using Label = Dialog::Label;
|
||||
|
||||
s.sub_scope<Hbox>([&] (Scope<Button, Hbox> &s) {
|
||||
s.sub_scope<Left_floating_hbox>(
|
||||
[&] (Scope<Button, Hbox, Left_floating_hbox> &s) {
|
||||
@ -118,11 +117,11 @@ struct Sculpt::Block_device_button : Widget<Button>
|
||||
};
|
||||
|
||||
|
||||
namespace Sculpt { struct Block_devices_dialog; }
|
||||
namespace Sculpt { struct Block_devices_widget; }
|
||||
|
||||
struct Sculpt::Block_devices_dialog : Storage_devices_dialog_base
|
||||
struct Sculpt::Block_devices_widget : Storage_devices_widget_base
|
||||
{
|
||||
using Storage_devices_dialog_base::Storage_devices_dialog_base;
|
||||
using Storage_devices_widget_base::Storage_devices_widget_base;
|
||||
|
||||
void view(Scope<Vbox> &s) const
|
||||
{
|
||||
@ -133,12 +132,12 @@ struct Sculpt::Block_devices_dialog : Storage_devices_dialog_base
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Storage_device_dialog::Action &action)
|
||||
void click(Clicked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
_click_device<Block_device_button>(at, action);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, Storage_device_dialog::Action &action)
|
||||
void clack(Clacked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
_clack_device(at, action);
|
||||
}
|
||||
@ -157,8 +156,6 @@ struct Sculpt::Usb_storage_device_button : Widget<Button>
|
||||
if (s.hovered() && !discarded) s.attribute("hovered", "yes");
|
||||
if (selected) s.attribute("selected", "yes");
|
||||
|
||||
using Label = Dialog::Label;
|
||||
|
||||
s.sub_scope<Hbox>([&] (Scope<Button, Hbox> &s) {
|
||||
s.sub_scope<Left_floating_hbox>(
|
||||
[&] (Scope<Button, Hbox, Left_floating_hbox> &s) {
|
||||
@ -183,11 +180,11 @@ struct Sculpt::Usb_storage_device_button : Widget<Button>
|
||||
};
|
||||
|
||||
|
||||
namespace Sculpt { struct Usb_devices_dialog; }
|
||||
namespace Sculpt { struct Usb_devices_widget; }
|
||||
|
||||
struct Sculpt::Usb_devices_dialog : Storage_devices_dialog_base
|
||||
struct Sculpt::Usb_devices_widget : Storage_devices_widget_base
|
||||
{
|
||||
using Storage_devices_dialog_base::Storage_devices_dialog_base;
|
||||
using Storage_devices_widget_base::Storage_devices_widget_base;
|
||||
|
||||
void view(Scope<Vbox> &s) const
|
||||
{
|
||||
@ -198,15 +195,15 @@ struct Sculpt::Usb_devices_dialog : Storage_devices_dialog_base
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Storage_device_dialog::Action &action)
|
||||
void click(Clicked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
_click_device<Usb_storage_device_button>(at, action);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, Storage_device_dialog::Action &action)
|
||||
void clack(Clacked_at const &at, Storage_device_widget::Action &action)
|
||||
{
|
||||
_clack_device(at, action);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__STORAGE_DIALOG_H_ */
|
||||
#endif /* _VIEW__STORAGE_WIDGET_H_ */
|
@ -14,105 +14,83 @@
|
||||
#ifndef _VIEW__SYSTEM_DIALOG_H_
|
||||
#define _VIEW__SYSTEM_DIALOG_H_
|
||||
|
||||
#include <view/layout_helper.h>
|
||||
#include <view/dialog.h>
|
||||
#include <view/software_presets_dialog.h>
|
||||
#include <view/software_update_dialog.h>
|
||||
#include <view/software_version_dialog.h>
|
||||
#include <view/software_presets_widget.h>
|
||||
#include <view/software_update_widget.h>
|
||||
#include <view/software_version_widget.h>
|
||||
#include <model/settings.h>
|
||||
|
||||
namespace Sculpt { struct System_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::System_dialog : Noncopyable, Deprecated_dialog
|
||||
struct Sculpt::System_dialog : Top_level_dialog
|
||||
{
|
||||
using Depot_users = Depot_users_dialog::Depot_users;
|
||||
using Depot_users = Depot_users_widget::Depot_users;
|
||||
using Image_index = Attached_rom_dataspace;
|
||||
|
||||
Hoverable_item _tab_item { };
|
||||
Presets const &_presets;
|
||||
Image_index const &_image_index;
|
||||
Build_info const &_build_info;
|
||||
|
||||
enum Tab { PRESETS, UPDATE } _selected_tab = Tab::PRESETS;
|
||||
Software_presets_widget::Action &_presets_action;
|
||||
Software_update_widget::Action &_update_action;
|
||||
|
||||
Software_presets_dialog _presets_dialog;
|
||||
Software_update_dialog _update_dialog;
|
||||
Software_version_dialog _version_dialog;
|
||||
enum Tab { PRESET, UPDATE } _selected_tab = Tab::PRESET;
|
||||
|
||||
Hover_result hover(Xml_node hover) override
|
||||
Hosted<Frame, Vbox, Software_presets_widget> _presets_widget { Id { "presets" } };
|
||||
Hosted<Frame, Vbox, Software_update_widget> _update_widget;
|
||||
Hosted<Frame, Vbox, Software_version_widget> _version_widget { Id { "version" } };
|
||||
|
||||
Hosted<Frame, Vbox, Hbox, Select_button<Tab>>
|
||||
_preset_tab { Id { " Presets " }, Tab::PRESET },
|
||||
_update_tab { Id { " Update " }, Tab::UPDATE };
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
Hover_result dialog_hover_result = Hover_result::UNMODIFIED;
|
||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||
|
||||
hover.with_optional_sub_node("frame", [&] (Xml_node const &frame) {
|
||||
frame.with_optional_sub_node("vbox", [&] (Xml_node const &vbox) {
|
||||
switch (_selected_tab) {
|
||||
case Tab::PRESETS: dialog_hover_result = _presets_dialog.hover(vbox); break;
|
||||
case Tab::UPDATE: dialog_hover_result = _update_dialog.hover(vbox); break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return any_hover_changed(
|
||||
dialog_hover_result,
|
||||
_tab_item.match(hover, "frame", "vbox", "hbox", "button", "name"));
|
||||
}
|
||||
|
||||
void reset() override { }
|
||||
|
||||
void generate(Xml_generator &xml) const override
|
||||
{
|
||||
gen_named_node(xml, "frame", "system", [&] {
|
||||
xml.node("vbox", [&] {
|
||||
gen_named_node(xml, "hbox", "tabs", [&] {
|
||||
auto gen_tab = [&] (auto const &id, auto tab, auto const &text)
|
||||
{
|
||||
gen_named_node(xml, "button", id, [&] {
|
||||
_tab_item.gen_hovered_attr(xml, id);
|
||||
if (_selected_tab == tab)
|
||||
xml.attribute("selected", "yes");
|
||||
xml.node("label", [&] {
|
||||
xml.attribute("text", text);
|
||||
});
|
||||
});
|
||||
};
|
||||
gen_tab("presets", Tab::PRESETS, " Presets ");
|
||||
gen_tab("update", Tab::UPDATE, " Update ");
|
||||
/* tabs */
|
||||
s.sub_scope<Hbox>([&] (Scope<Frame, Vbox, Hbox> &s) {
|
||||
s.widget(_preset_tab, _selected_tab, [&] (auto &s) {
|
||||
if (!_presets.available())
|
||||
s.attribute("style", "unimportant");
|
||||
s.template sub_scope<Label>(_preset_tab.id.value);
|
||||
});
|
||||
s.widget(_update_tab, _selected_tab);
|
||||
});
|
||||
|
||||
switch (_selected_tab) {
|
||||
case Tab::PRESETS:
|
||||
_presets_dialog.generate(xml);
|
||||
case Tab::PRESET:
|
||||
s.widget(_presets_widget, _presets);
|
||||
break;
|
||||
case Tab::UPDATE:
|
||||
_update_dialog .generate(xml);
|
||||
_version_dialog.generate(xml);
|
||||
case Tab::UPDATE:
|
||||
s.widget(_update_widget, _image_index.xml());
|
||||
s.widget(_version_widget, _build_info);
|
||||
break;
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Click_result click()
|
||||
void click(Clicked_at const &at) override
|
||||
{
|
||||
if (_tab_item._hovered.valid()) {
|
||||
if (_tab_item._hovered == "presets") _selected_tab = Tab::PRESETS;
|
||||
if (_tab_item._hovered == "update") _selected_tab = Tab::UPDATE;
|
||||
return Click_result::CONSUMED;
|
||||
};
|
||||
_preset_tab.propagate(at, [&] (Tab t) { _selected_tab = t; });
|
||||
_update_tab.propagate(at, [&] (Tab t) { _selected_tab = t; });
|
||||
|
||||
switch (_selected_tab) {
|
||||
case Tab::PRESETS: _presets_dialog.click(); break;
|
||||
case Tab::UPDATE: _update_dialog .click(); break;
|
||||
}
|
||||
return Click_result::CONSUMED;
|
||||
_presets_widget.propagate(at, _presets);
|
||||
|
||||
if (_selected_tab == Tab::UPDATE)
|
||||
_update_widget.propagate(at, _update_action);
|
||||
}
|
||||
|
||||
Click_result clack()
|
||||
void clack(Clacked_at const &at) override
|
||||
{
|
||||
switch (_selected_tab) {
|
||||
case Tab::PRESETS: _presets_dialog.clack(); break;
|
||||
case Tab::UPDATE: _update_dialog .clack(); break;
|
||||
};
|
||||
return Click_result::CONSUMED;
|
||||
_presets_widget.propagate(at, _presets, _presets_action);
|
||||
}
|
||||
|
||||
void drag(Dragged_at const &) override { }
|
||||
|
||||
System_dialog(Presets const &presets,
|
||||
Build_info const &build_info,
|
||||
Nic_state const &nic_state,
|
||||
@ -121,26 +99,30 @@ struct Sculpt::System_dialog : Noncopyable, Deprecated_dialog
|
||||
File_operation_queue const &file_operation_queue,
|
||||
Depot_users const &depot_users,
|
||||
Image_index const &image_index,
|
||||
Software_presets_dialog::Action &presets_action,
|
||||
Depot_users_dialog::Action &depot_users_action,
|
||||
Software_update_dialog::Action &update_action)
|
||||
Software_presets_widget::Action &presets_action,
|
||||
Software_update_widget::Action &update_action)
|
||||
:
|
||||
_presets_dialog(presets, presets_action),
|
||||
_update_dialog(build_info, nic_state, download_queue,
|
||||
Top_level_dialog("system"),
|
||||
_presets(presets), _image_index(image_index), _build_info(build_info),
|
||||
_presets_action(presets_action), _update_action(update_action),
|
||||
_update_widget(Id { "update" },
|
||||
build_info, nic_state, download_queue,
|
||||
index_update_queue, file_operation_queue, depot_users,
|
||||
image_index, depot_users_action, update_action),
|
||||
_version_dialog(build_info)
|
||||
image_index)
|
||||
{ }
|
||||
|
||||
bool update_tab_selected() const { return _selected_tab == Tab::UPDATE; }
|
||||
|
||||
bool keyboard_needed() const { return _update_dialog.keyboard_needed(); }
|
||||
bool keyboard_needed() const { return _update_widget.keyboard_needed(); }
|
||||
|
||||
void handle_key(Codepoint c) { _update_dialog.handle_key(c); }
|
||||
void handle_key(Codepoint c, Software_update_widget::Action &action)
|
||||
{
|
||||
_update_widget.handle_key(c, action);
|
||||
}
|
||||
|
||||
void sanitize_user_selection() { _update_dialog.sanitize_user_selection(); }
|
||||
void sanitize_user_selection() { _update_widget.sanitize_user_selection(); }
|
||||
|
||||
void reset_update_dialog() { _update_dialog.reset(); }
|
||||
void reset_update_widget() { _update_widget.reset(); }
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SYSTEM_DIALOG_H_ */
|
||||
|
@ -72,7 +72,7 @@ namespace Sculpt {
|
||||
|
||||
static inline void gen_parent_rom_route(Xml_generator &xml,
|
||||
Rom_name const &name,
|
||||
Label const &label)
|
||||
auto const &label)
|
||||
{
|
||||
gen_service_node<Rom_session>(xml, [&] () {
|
||||
xml.attribute("label_last", name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user