From 1d3ce93107a9b218fc98402b972cc76f9f21bdd2 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Sun, 26 Jan 2020 21:14:40 +0100 Subject: [PATCH] sculpt: leitzentrale user-interface redesign Issue #3650 --- repos/gems/recipes/pkg/sculpt/archives | 3 + repos/gems/run/leitzentrale.run | 48 +- repos/gems/run/sculpt.run | 33 +- repos/gems/run/sculpt/leitzentrale.config | 100 ++- repos/gems/run/sculpt/nitpicker.config | 2 +- repos/gems/src/app/sculpt_manager/deploy.h | 12 +- repos/gems/src/app/sculpt_manager/graph.cc | 374 ++++++++ repos/gems/src/app/sculpt_manager/graph.h | 314 ++----- repos/gems/src/app/sculpt_manager/gui.cc | 114 --- repos/gems/src/app/sculpt_manager/gui.h | 65 -- .../src/app/sculpt_manager/keyboard_focus.h | 16 +- repos/gems/src/app/sculpt_manager/main.cc | 807 +++++++++++++----- .../gems/src/app/sculpt_manager/menu_view.cc | 152 ++++ repos/gems/src/app/sculpt_manager/menu_view.h | 72 ++ .../app/sculpt_manager/model/child_state.h | 17 +- .../sculpt_manager/model/file_browser_state.h | 217 +++++ .../src/app/sculpt_manager/model/partition.h | 5 +- .../app/sculpt_manager/model/ram_fs_state.h | 12 +- .../app/sculpt_manager/model/runtime_config.h | 26 +- .../app/sculpt_manager/model/runtime_state.h | 17 + .../src/app/sculpt_manager/model/service.h | 6 + .../src/app/sculpt_manager/model/settings.h | 23 + .../app/sculpt_manager/model/storage_device.h | 4 +- .../sculpt_manager/model/storage_devices.h | 6 + .../sculpt_manager/model/usb_storage_device.h | 10 +- repos/gems/src/app/sculpt_manager/network.cc | 10 +- repos/gems/src/app/sculpt_manager/network.h | 26 +- .../sculpt_manager/runtime/file_browser.cc | 2 +- .../sculpt_manager/runtime/runtime_view.cc | 6 +- repos/gems/src/app/sculpt_manager/storage.cc | 2 +- repos/gems/src/app/sculpt_manager/storage.h | 21 +- .../gems/src/app/sculpt_manager/view/dialog.h | 65 +- .../sculpt_manager/view/file_browser_dialog.h | 381 +++++++++ .../src/app/sculpt_manager/view/fs_dialog.h | 89 ++ .../app/sculpt_manager/view/hoverable_item.h | 6 +- .../app/sculpt_manager/view/network_dialog.cc | 48 +- .../app/sculpt_manager/view/network_dialog.h | 23 +- .../app/sculpt_manager/view/panel_dialog.cc | 87 ++ .../app/sculpt_manager/view/panel_dialog.h | 81 ++ .../sculpt_manager/view/partition_dialog.cc | 238 ++++++ .../sculpt_manager/view/partition_dialog.h | 77 ++ .../app/sculpt_manager/view/popup_dialog.cc | 2 - .../app/sculpt_manager/view/popup_dialog.h | 72 +- .../app/sculpt_manager/view/ram_fs_dialog.h | 119 +++ .../app/sculpt_manager/view/settings_dialog.h | 107 +++ .../view/storage_device_dialog.cc | 122 +++ .../view/storage_device_dialog.h | 78 ++ .../app/sculpt_manager/view/storage_dialog.cc | 453 +--------- .../app/sculpt_manager/view/storage_dialog.h | 85 +- 49 files changed, 3312 insertions(+), 1343 deletions(-) create mode 100644 repos/gems/src/app/sculpt_manager/graph.cc delete mode 100644 repos/gems/src/app/sculpt_manager/gui.cc delete mode 100644 repos/gems/src/app/sculpt_manager/gui.h create mode 100644 repos/gems/src/app/sculpt_manager/menu_view.cc create mode 100644 repos/gems/src/app/sculpt_manager/menu_view.h create mode 100644 repos/gems/src/app/sculpt_manager/model/file_browser_state.h create mode 100644 repos/gems/src/app/sculpt_manager/model/settings.h create mode 100644 repos/gems/src/app/sculpt_manager/view/file_browser_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/fs_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/panel_dialog.cc create mode 100644 repos/gems/src/app/sculpt_manager/view/panel_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/partition_dialog.cc create mode 100644 repos/gems/src/app/sculpt_manager/view/partition_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/ram_fs_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/settings_dialog.h create mode 100644 repos/gems/src/app/sculpt_manager/view/storage_device_dialog.cc create mode 100644 repos/gems/src/app/sculpt_manager/view/storage_device_dialog.h diff --git a/repos/gems/recipes/pkg/sculpt/archives b/repos/gems/recipes/pkg/sculpt/archives index 21c673f1d..cc7c81ee4 100644 --- a/repos/gems/recipes/pkg/sculpt/archives +++ b/repos/gems/recipes/pkg/sculpt/archives @@ -2,6 +2,7 @@ _/pkg/drivers_managed-pc _/pkg/wifi _/pkg/depot_download _/pkg/terminal +_/pkg/backdrop _/src/report_rom _/src/clipboard _/src/init @@ -48,3 +49,5 @@ _/src/gpt_write _/src/sculpt_manager _/src/fs_query _/src/fs_tool +_/src/text_area +_/src/sandbox diff --git a/repos/gems/run/leitzentrale.run b/repos/gems/run/leitzentrale.run index 02b727f80..e9767bcd8 100644 --- a/repos/gems/run/leitzentrale.run +++ b/repos/gems/run/leitzentrale.run @@ -3,6 +3,7 @@ create_boot_directory import_from_depot [depot_user]/src/[base_src] \ [depot_user]/pkg/[drivers_interactive_pkg] \ [depot_user]/pkg/fonts_fs \ + [depot_user]/pkg/backdrop \ [depot_user]/src/dynamic_rom \ [depot_user]/src/report_rom \ [depot_user]/src/fs_rom \ @@ -19,6 +20,7 @@ import_from_depot [depot_user]/src/[base_src] \ [depot_user]/src/libpng \ [depot_user]/src/zlib \ [depot_user]/src/menu_view \ + [depot_user]/src/fs_query \ [depot_user]/src/rom_filter \ [depot_user]/src/noux \ [depot_user]/src/terminal \ @@ -33,6 +35,8 @@ import_from_depot [depot_user]/src/[base_src] \ [depot_user]/src/coreutils-minimal \ [depot_user]/src/e2fsprogs-minimal \ [depot_user]/src/gpt_write \ + [depot_user]/src/text_area \ + [depot_user]/src/sandbox \ [depot_user]/src/window_layouter install_config { @@ -82,10 +86,7 @@ install_config { report="nitpicker -> hover"/> - - + @@ -148,8 +149,9 @@ install_config { - See the core log for messages. + + @@ -191,12 +193,13 @@ install_config { + - + @@ -219,10 +222,15 @@ install_config { - + - - + + + + + + + @@ -233,9 +241,7 @@ install_config { - - - + @@ -253,8 +259,6 @@ install_config { - - @@ -267,14 +271,20 @@ install_config { - - - - + + + + + + + + + + @@ -282,6 +292,8 @@ install_config { + + diff --git a/repos/gems/run/sculpt.run b/repos/gems/run/sculpt.run index 546a63cc3..24789588e 100644 --- a/repos/gems/run/sculpt.run +++ b/repos/gems/run/sculpt.run @@ -75,10 +75,6 @@ install_config { report="global_keys_handler -> slides"/> - - @@ -397,11 +393,16 @@ install_config { - + - + - + + + + + + @@ -418,10 +419,6 @@ install_config { - - - - @@ -452,8 +449,6 @@ install_config { - - @@ -474,12 +469,20 @@ install_config { + + + + - - + + + + + + diff --git a/repos/gems/run/sculpt/leitzentrale.config b/repos/gems/run/sculpt/leitzentrale.config index d463d9b09..d0755d3c9 100644 --- a/repos/gems/run/sculpt/leitzentrale.config +++ b/repos/gems/run/sculpt/leitzentrale.config @@ -23,6 +23,14 @@ + + + + + + + + @@ -32,7 +40,7 @@ - + @@ -46,7 +54,7 @@ - + @@ -83,7 +91,7 @@ - + @@ -94,19 +102,42 @@ - - - - - + + + + + + + + + + + + + + - - + + @@ -123,9 +154,9 @@ - + - + @@ -140,10 +171,14 @@ - - - - + + + + + + + + @@ -192,13 +227,10 @@ - - - - + @@ -217,19 +249,6 @@ - - - - - - - - - - - - - @@ -285,6 +304,21 @@ + + + + + + + + + + + + + + diff --git a/repos/gems/run/sculpt/nitpicker.config b/repos/gems/run/sculpt/nitpicker.config index fdb0dfe43..9d8c3ea80 100644 --- a/repos/gems/run/sculpt/nitpicker.config +++ b/repos/gems/run/sculpt/nitpicker.config @@ -1,6 +1,6 @@ - + diff --git a/repos/gems/src/app/sculpt_manager/deploy.h b/repos/gems/src/app/sculpt_manager/deploy.h index c8805b29f..2c636abfa 100644 --- a/repos/gems/src/app/sculpt_manager/deploy.h +++ b/repos/gems/src/app/sculpt_manager/deploy.h @@ -39,6 +39,8 @@ struct Sculpt::Deploy Allocator &_alloc; + Registry &_child_states; + Runtime_info const &_runtime_info; Dialog::Generator &_dialog_generator; @@ -56,10 +58,10 @@ struct Sculpt::Deploy Arch _arch { }; Child_state cached_depot_rom_state { - "depot_rom", Ram_quota{24*1024*1024}, Cap_quota{200} }; + _child_states, "depot_rom", Ram_quota{24*1024*1024}, Cap_quota{200} }; Child_state uncached_depot_rom_state { - "dynamic_depot_rom", Ram_quota{8*1024*1024}, Cap_quota{200} }; + _child_states, "dynamic_depot_rom", Ram_quota{8*1024*1024}, Cap_quota{200} }; /* * Report written to '/config/managed/deploy' @@ -227,7 +229,8 @@ struct Sculpt::Deploy }); } - Deploy(Env &env, Allocator &alloc, Runtime_info const &runtime_info, + Deploy(Env &env, Allocator &alloc, Registry &child_states, + Runtime_info const &runtime_info, Dialog::Generator &dialog_generator, Runtime_config_generator &runtime_config_generator, Depot_query &depot_query, @@ -235,7 +238,8 @@ struct Sculpt::Deploy Attached_rom_dataspace const &blueprint_rom, Download_queue const &download_queue) : - _env(env), _alloc(alloc), _runtime_info(runtime_info), + _env(env), _alloc(alloc), _child_states(child_states), + _runtime_info(runtime_info), _dialog_generator(dialog_generator), _runtime_config_generator(runtime_config_generator), _depot_query(depot_query), diff --git a/repos/gems/src/app/sculpt_manager/graph.cc b/repos/gems/src/app/sculpt_manager/graph.cc new file mode 100644 index 000000000..941b9706c --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/graph.cc @@ -0,0 +1,374 @@ +/* + * \brief Graph view of runtime state + * \author Norman Feske + * \date 2018-07-05 + */ + +/* + * 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 +#include + +using namespace Sculpt; + + +void Graph::_gen_selected_node_content(Xml_generator &xml, Start_name const &name, + Runtime_state::Info const &info) const +{ + bool const removable = _deploy_children.exists(name); + + if (removable) { + gen_named_node(xml, "frame", "operations", [&] () { + xml.node("vbox", [&] () { + gen_named_node(xml, "button", "remove", [&] () { + _remove_item.gen_button_attr(xml, "remove"); + xml.node("label", [&] () { + xml.attribute("text", "Remove"); + }); + }); + }); + }); + } + + if (name == "ram_fs") + gen_named_node(xml, "frame", "ram_fs_operations", [&] () { + xml.node("vbox", [&] () { + _ram_fs_dialog.generate(xml, _ram_fs_state); }); }); + + String<100> const + ram (Capacity{info.assigned_ram - info.avail_ram}, " / ", + Capacity{info.assigned_ram}), + caps(info.assigned_caps - info.avail_caps, " / ", + info.assigned_caps, " caps"); + + gen_named_node(xml, "label", "hspace", [&] () { + xml.attribute("min_ex", 25); }); + + gen_named_node(xml, "label", "ram", [&] () { + xml.attribute("text", ram); }); + + gen_named_node(xml, "label", "caps", [&] () { + xml.attribute("text", caps); }); +} + + +void Graph::_gen_parent_node(Xml_generator &xml, Start_name const &name, + Label const &label) const +{ + gen_named_node(xml, "frame", name, [&] () { + xml.node("label", [&] () { + xml.attribute("text", Start_name(" ", label, " ")); }); }); +} + + +void Graph::_gen_storage_node(Xml_generator &xml) const +{ + char const * const name = "storage"; + + bool const any_selected = _runtime_state.selected().valid(); + bool const unimportant = any_selected && !_runtime_state.storage_in_tcb(); + + gen_named_node(xml, "frame", name, [&] () { + + if (unimportant) + xml.attribute("style", "unimportant"); + + xml.node("vbox", [&] () { + + gen_named_node(xml, "button", name, [&] () { + + _node_button_item.gen_button_attr(xml, name); + + if (unimportant) + xml.attribute("style", "unimportant"); + + if (_storage_selected) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", "Storage"); }); + }); + + if (_storage_selected) + gen_named_node(xml, "frame", "storage_operations", [&] () { + xml.node("vbox", [&] () { + _storage_dialog->gen_block_devices(xml); }); }); + }); + }); +} + + +void Graph::_gen_usb_node(Xml_generator &xml) const +{ + char const * const name = "usb"; + + bool const any_selected = _runtime_state.selected().valid(); + bool const unimportant = any_selected && !_runtime_state.usb_in_tcb(); + + gen_named_node(xml, "frame", name, [&] () { + + if (unimportant) + xml.attribute("style", "unimportant"); + + xml.node("vbox", [&] () { + + gen_named_node(xml, "button", name, [&] () { + + _node_button_item.gen_button_attr(xml, name); + + if (unimportant) + xml.attribute("style", "unimportant"); + + if (_usb_selected) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", "USB"); }); + }); + + if (_usb_selected) + gen_named_node(xml, "frame", "usb_operations", [&] () { + xml.node("vbox", [&] () { + _storage_dialog->gen_usb_storage_devices(xml); }); }); + }); + }); +} + + +void Graph::generate(Xml_generator &xml) const +{ + xml.node("depgraph", [&] () { + + if (_sculpt_partition.valid()) { + gen_named_node(xml, "button", "global+", [&] () { + _add_button_item.gen_button_attr(xml, "global+"); + + if (_popup_state == Popup::VISIBLE) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { + xml.attribute("text", "+"); }); }); + } + + _gen_storage_node(xml); + + if (_storage_devices.usb_present) + _gen_usb_node(xml); + else + _gen_parent_node(xml, "usb", "USB"); + + /* parent roles */ + _gen_parent_node(xml, "hardware", "Hardware"); + _gen_parent_node(xml, "config", "Config"); + _gen_parent_node(xml, "info", "Info"); + _gen_parent_node(xml, "GUI", "GUI"); + + typedef Runtime_config::Component Component; + + bool const any_selected = _runtime_state.selected().valid(); + + _runtime_config.for_each_component([&] (Component const &component) { + + Start_name const name = component.name; + Start_name const pretty_name { Pretty(name) }; + + /* omit sculpt's helpers from the graph */ + bool const blacklisted = (name == "runtime_view" + || name == "popup_view" + || name == "menu_view" + || name == "panel_view" + || name == "settings_view" + || name == "network_view" + || name == "file_browser_view" + || name == "editor" + || name == "launcher_query" + || name == "update" + || name == "fs_tool" + || name == "depot_rw" + || name == "public_rw" + || name == "depot_rom" + || name == "dynamic_depot_rom" + || name == "depot_query"); + if (blacklisted) + return; + + Runtime_state::Info const info = _runtime_state.info(name); + + bool const unimportant = any_selected && !info.tcb; + + gen_named_node(xml, "frame", name, [&] () { + + if (unimportant) + xml.attribute("style", "unimportant"); + + Start_name primary_dep = component.primary_dependency; + + if (primary_dep == "default_fs_rw") + primary_dep = _sculpt_partition.fs(); + + if (primary_dep.valid()) { + xml.attribute("dep", primary_dep); + if (unimportant) + xml.attribute("dep_visible", false); + } + + xml.node("vbox", [&] () { + + gen_named_node(xml, "button", name, [&] () { + + if (unimportant) + xml.attribute("style", "unimportant"); + + _node_button_item.gen_button_attr(xml, name); + + if (info.selected) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { + xml.attribute("text", pretty_name); + }); + }); + + if (info.selected) + _gen_selected_node_content(xml, name, info); + }); + }); + }); + + _runtime_config.for_each_component([&] (Component const &component) { + + Start_name const name = component.name; + + Runtime_state::Info const info = _runtime_state.info(name); + + bool const show_details = info.tcb; + + if (show_details) { + component.for_each_secondary_dep([&] (Start_name dep) { + + if (Runtime_state::blacklisted_from_graph(dep)) + return; + + if (dep == "default_fs_rw") + dep = _sculpt_partition.fs(); + + xml.node("dep", [&] () { + xml.attribute("node", name); + xml.attribute("on", dep); + }); + }); + } + }); + }); +} + + +Dialog::Hover_result Graph::hover(Xml_node hover) +{ + Hover_result const storage_dialog_hover_result = + _storage_dialog->match_sub_dialog(hover, "depgraph", "frame", "vbox", "frame", "vbox"); + + Dialog::Hover_result const hover_result = Dialog::any_hover_changed( + storage_dialog_hover_result, + _ram_fs_dialog.match_sub_dialog(hover, "depgraph", "frame", "vbox", "frame", "vbox"), + _node_button_item.match(hover, "depgraph", "frame", "vbox", "button", "name"), + _add_button_item .match(hover, "depgraph", "button", "name"), + _remove_item .match(hover, "depgraph", "frame", "vbox", + "frame", "vbox", "button", "name")); + + if (_add_button_item.hovered("global+")) { + + /* update anchor geometry of popup menu */ + auto hovered_rect = [] (Xml_node const hover) { + + auto point_from_xml = [] (Xml_node node) { + return Point(node.attribute_value("xpos", 0L), + node.attribute_value("ypos", 0L)); }; + + auto area_from_xml = [] (Xml_node node) { + return Area(node.attribute_value("width", 0UL), + node.attribute_value("height", 0UL)); }; + + if (!hover.has_sub_node("dialog")) return Rect(); + Xml_node const dialog = hover.sub_node("dialog"); + + if (!dialog.has_sub_node("depgraph")) return Rect(); + Xml_node const depgraph = dialog.sub_node("depgraph"); + + if (!depgraph.has_sub_node("button")) return Rect(); + Xml_node const button = depgraph.sub_node("button"); + + return Rect(point_from_xml(dialog) + point_from_xml(depgraph) + + point_from_xml(button), + area_from_xml(button)); + }; + + _popup_anchor = hovered_rect(hover); + } + + return hover_result; +} + + +void Graph::click(Action &action) +{ + if (_ram_fs_dialog.click(action) == Click_result::CONSUMED) + return; + + if (_storage_dialog_visible()) + if (_storage_dialog->click(action) == Click_result::CONSUMED) + return; + + if (_add_button_item._hovered.valid()) + action.toggle_launcher_selector(_popup_anchor); + + if (_node_button_item._hovered.valid()) { + + _storage_selected = !_storage_selected && _node_button_item.hovered("storage"); + _usb_selected = !_usb_selected && _node_button_item.hovered("usb"); + + /* reset storage dialog */ + if (_usb_selected || _storage_selected) + _storage_dialog.construct(_storage_devices, _sculpt_partition); + + _runtime_state.toggle_selection(_node_button_item._hovered, + _runtime_config); + _remove_item.reset(); + } + + if (_remove_item.hovered("remove")) + _remove_item.propose_activation_on_click(); +} + + +void Graph::clack(Action &action, Ram_fs_dialog::Action &ram_fs_action) +{ + if (_ram_fs_dialog.clack(ram_fs_action) == Clack_result::CONSUMED) + return; + + if (_storage_dialog_visible()) + if (_storage_dialog->clack(action) == Clack_result::CONSUMED) + return; + + if (_remove_item.hovered("remove")) { + + _remove_item.confirm_activation_on_clack(); + + if (_remove_item.activated("remove")) { + action.remove_deployed_component(_runtime_state.selected()); + + /* + * Unselect the removed component to bring graph into + * default state. + */ + _runtime_state.toggle_selection(_runtime_state.selected(), + _runtime_config); + } + } else { + _remove_item.reset(); + } +} + diff --git a/repos/gems/src/app/sculpt_manager/graph.h b/repos/gems/src/app/sculpt_manager/graph.h index 4803f0f33..04989b1ca 100644 --- a/repos/gems/src/app/sculpt_manager/graph.h +++ b/repos/gems/src/app/sculpt_manager/graph.h @@ -2,9 +2,6 @@ * \brief Graph view of runtime state * \author Norman Feske * \date 2018-07-05 - * - * The GUI is based on a dynamically configured init component, which hosts - * one menu-view component for each dialog. */ /* @@ -19,38 +16,40 @@ /* Genode includes */ #include +#include + +/* included from depot_deploy tool */ +#include /* local includes */ #include #include +#include +#include +#include #include #include #include +#include +#include #include namespace Sculpt { struct Graph; } -struct Sculpt::Graph +struct Sculpt::Graph : Dialog { - Env &_env; - Runtime_state &_runtime_state; Runtime_config const &_runtime_config; - Storage_target const &_sculpt_partition; + Storage_devices const &_storage_devices; + Storage_target const &_sculpt_partition; + Ram_fs_state const &_ram_fs_state; Popup::State const &_popup_state; Depot_deploy::Children const &_deploy_children; - Expanding_reporter _graph_dialog_reporter { _env, "dialog", "runtime_view_dialog" }; - - Attached_rom_dataspace _hover_rom { _env, "runtime_view_hover" }; - - Signal_handler _hover_handler { - _env.ep(), *this, &Graph::_handle_hover }; - Hoverable_item _node_button_item { }; Hoverable_item _add_button_item { }; Activatable_item _remove_item { }; @@ -60,275 +59,58 @@ struct Sculpt::Graph */ Rect _popup_anchor { }; - bool _hovered = false; + Ram_fs_dialog _ram_fs_dialog; - void _gen_selected_node_content(Xml_generator &xml, Start_name const &name, - Runtime_state::Info const &info) const + bool _storage_selected = false; + bool _usb_selected = false; + + bool _storage_dialog_visible() const { return _storage_selected || _usb_selected; } + + Reconstructible _storage_dialog { _storage_devices, _sculpt_partition }; + + void _gen_selected_node_content(Xml_generator &, Start_name const &, + Runtime_state::Info const &) const; + + void _gen_parent_node(Xml_generator &, Start_name const &, Label const &) const; + void _gen_storage_node(Xml_generator &) const; + void _gen_usb_node(Xml_generator &) const; + + void generate(Xml_generator &) const override; + + Hover_result hover(Xml_node) override; + + void reset() override { } + + void reset_operation() { - bool const removable = _deploy_children.exists(name); - - if (removable) { - gen_named_node(xml, "frame", "operations", [&] () { - xml.node("vbox", [&] () { - gen_named_node(xml, "button", "remove", [&] () { - _remove_item.gen_button_attr(xml, "remove"); - xml.node("label", [&] () { - xml.attribute("text", "Remove"); - }); - }); - }); - }); - } - - String<100> const - ram (Capacity{info.assigned_ram - info.avail_ram}, " / ", - Capacity{info.assigned_ram}), - caps(info.assigned_caps - info.avail_caps, " / ", - info.assigned_caps, " caps"); - - gen_named_node(xml, "label", "ram", [&] () { - xml.attribute("text", ram); }); - - gen_named_node(xml, "label", "caps", [&] () { - xml.attribute("text", caps); }); + if (_storage_dialog.constructed()) + _storage_dialog->reset_operation(); } - void _gen_parent_node(Xml_generator &xml, Start_name const &name) const - { - gen_named_node(xml, "frame", name, [&] () { - xml.node("label", [&] () { - xml.attribute("text", Start_name(" ", name, " ")); }); }); - } - - void _gen_graph_dialog() - { - _graph_dialog_reporter.generate([&] (Xml_generator &xml) { - - xml.node("depgraph", [&] () { - - if (_sculpt_partition.valid()) { - gen_named_node(xml, "button", "global+", [&] () { - _add_button_item.gen_button_attr(xml, "global+"); - - if (_popup_state == Popup::VISIBLE) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { - xml.attribute("text", "+"); }); }); - } - - /* parent roles */ - _gen_parent_node(xml, "hardware"); - _gen_parent_node(xml, "config"); - _gen_parent_node(xml, "info"); - _gen_parent_node(xml, "GUI"); - - typedef Runtime_config::Component Component; - - bool const any_selected = _runtime_state.selected().valid(); - - _runtime_config.for_each_component([&] (Component const &component) { - - Start_name const name = component.name; - Start_name const pretty_name { Pretty(name) }; - - /* omit sculpt's helpers from the graph */ - bool const blacklisted = (name == "runtime_view" - || name == "launcher_query" - || name == "update" - || name == "fs_tool" - || name == "depot_rw" - || name == "public_rw" - || name == "depot_rom" - || name == "dynamic_depot_rom" - || name == "depot_query"); - if (blacklisted) - return; - - Runtime_state::Info const info = _runtime_state.info(name); - - bool const unimportant = any_selected && !info.tcb; - - gen_named_node(xml, "frame", name, [&] () { - - if (unimportant) - xml.attribute("style", "unimportant"); - - Start_name primary_dep = component.primary_dependency; - - if (primary_dep == "default_fs_rw") - primary_dep = _sculpt_partition.fs(); - - if (primary_dep.valid()) { - xml.attribute("dep", primary_dep); - if (unimportant) - xml.attribute("dep_visible", false); - } - - xml.node("vbox", [&] () { - - gen_named_node(xml, "button", name, [&] () { - - if (unimportant) - xml.attribute("style", "unimportant"); - - _node_button_item.gen_button_attr(xml, name); - - if (info.selected) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { - xml.attribute("text", pretty_name); - }); - }); - - if (info.selected) - _gen_selected_node_content(xml, name, info); - }); - }); - }); - - _runtime_config.for_each_component([&] (Component const &component) { - - Start_name const name = component.name; - - Runtime_state::Info const info = _runtime_state.info(name); - - bool const show_details = info.tcb; - - if (show_details) { - component.for_each_secondary_dep([&] (Start_name const &dep) { - - if (Runtime_state::blacklisted_from_graph(dep)) - return; - - xml.node("dep", [&] () { - xml.attribute("node", name); - xml.attribute("on", dep); - }); - }); - } - }); - }); - }); - } - - void _handle_hover() - { - _hover_rom.update(); - - Xml_node const hover = _hover_rom.xml(); - - _hovered = (hover.num_sub_nodes() != 0); - - bool const changed = - _node_button_item.match(hover, "dialog", "depgraph", "frame", "vbox", "button", "name") | - _add_button_item .match(hover, "dialog", "depgraph", "button", "name") | - _remove_item .match(hover, "dialog", "depgraph", "frame", "vbox", - "frame", "vbox", "button", "name"); - - if (_add_button_item.hovered("global+")) { - - /* update anchor geometry of popup menu */ - auto hovered_rect = [] (Xml_node const hover) { - - auto point_from_xml = [] (Xml_node node) { - return Point(node.attribute_value("xpos", 0L), - node.attribute_value("ypos", 0L)); }; - - auto area_from_xml = [] (Xml_node node) { - return Area(node.attribute_value("width", 0UL), - node.attribute_value("height", 0UL)); }; - - if (!hover.has_sub_node("dialog")) return Rect(); - Xml_node const dialog = hover.sub_node("dialog"); - - if (!dialog.has_sub_node("depgraph")) return Rect(); - Xml_node const depgraph = dialog.sub_node("depgraph"); - - if (!depgraph.has_sub_node("button")) return Rect(); - Xml_node const button = depgraph.sub_node("button"); - - return Rect(point_from_xml(dialog) + point_from_xml(depgraph) + - point_from_xml(button), - area_from_xml(button)); - }; - - _popup_anchor = hovered_rect(hover); - } - - if (changed) - _gen_graph_dialog(); - } - - Graph(Env &env, - Runtime_state &runtime_state, + Graph(Runtime_state &runtime_state, Runtime_config const &runtime_config, + Storage_devices const &storage_devices, Storage_target const &sculpt_partition, + Ram_fs_state const &ram_fs_state, Popup::State const &popup_state, Depot_deploy::Children const &deploy_children) : - _env(env), _runtime_state(runtime_state), _runtime_config(runtime_config), - _sculpt_partition(sculpt_partition), - _popup_state(popup_state), _deploy_children(deploy_children) - { - _hover_rom.sigh(_hover_handler); - } - - bool hovered() const { return _hovered; } + _runtime_state(runtime_state), _runtime_config(runtime_config), + _storage_devices(storage_devices), _sculpt_partition(sculpt_partition), + _ram_fs_state(ram_fs_state), _popup_state(popup_state), + _deploy_children(deploy_children), _ram_fs_dialog(sculpt_partition) + { } bool add_button_hovered() const { return _add_button_item._hovered.valid(); } - struct Action : Interface + struct Action : Storage_dialog::Action { virtual void remove_deployed_component(Start_name const &) = 0; virtual void toggle_launcher_selector(Rect) = 0; }; - void click(Action &action) - { - if (_add_button_item._hovered.valid()) { - action.toggle_launcher_selector(_popup_anchor); - } - - if (_node_button_item._hovered.valid()) { - _runtime_state.toggle_selection(_node_button_item._hovered, - _runtime_config); - _remove_item.reset(); - _gen_graph_dialog(); - } - - if (_remove_item.hovered("remove")) { - _remove_item.propose_activation_on_click(); - _gen_graph_dialog(); - } - } - - void clack(Action &action) - { - if (_remove_item.hovered("remove")) { - - _remove_item.confirm_activation_on_clack(); - - if (_remove_item.activated("remove")) { - action.remove_deployed_component(_runtime_state.selected()); - - /* - * Unselect the removed component to bring graph into - * default state. - */ - _runtime_state.toggle_selection(_runtime_state.selected(), - _runtime_config); - } - - } else { - _remove_item.reset(); - } - - _gen_graph_dialog(); - } - - void gen_dialog() { _gen_graph_dialog(); } + void click(Action &action); + void clack(Action &action, Ram_fs_dialog::Action &); }; #endif /* _GRAPH_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/gui.cc b/repos/gems/src/app/sculpt_manager/gui.cc deleted file mode 100644 index 3b6f81b45..000000000 --- a/repos/gems/src/app/sculpt_manager/gui.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * \brief Sculpt GUI management - * \author Norman Feske - * \date 2018-04-30 - * - * The GUI is based on a dynamically configured init component, which hosts - * one menu-view component for each dialog. - */ - -/* - * 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 - -/* local includes */ -#include - - -void Sculpt::Gui::_gen_menu_view_start_content(Xml_generator &xml, - Label const &label, - Point pos, - unsigned width) const -{ - xml.attribute("version", version.value); - - gen_common_start_content(xml, label, Cap_quota{150}, Ram_quota{12*1024*1024}); - - gen_named_node(xml, "binary", "menu_view"); - - xml.node("config", [&] () { - xml.attribute("xpos", pos.x()); - xml.attribute("ypos", pos.y()); - if (width) - xml.attribute("width", width); - 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", "styles", [&] () { - gen_named_node(xml, "dir", "frame", [&] () { - gen_named_node(xml, "dir", "logo", [&] () { - gen_named_node(xml, "rom", "background.png", [&] () { - xml.attribute("label", "genode_logo.png"); }); }); }); }); - - 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_rom_route(xml, "genode_logo.png"); - gen_parent_route (xml); - gen_parent_route (xml); - gen_parent_route (xml); - gen_parent_route (xml); - gen_parent_route (xml); - - gen_service_node(xml, [&] () { - xml.attribute("label", "dialog"); - xml.node("parent", [&] () { }); }); - - gen_service_node(xml, [&] () { - xml.attribute("label", "hover"); - xml.node("parent", [&] () { }); }); - - gen_service_node<::File_system::Session>(xml, [&] () { - xml.attribute("label", "fonts"); - xml.node("parent", [&] () { - xml.attribute("label", "fonts"); }); }); - }); -} - - -void Sculpt::Gui::_generate_config(Xml_generator &xml) const -{ - xml.node("parent-provides", [&] () { - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service(xml); - gen_parent_service<::File_system::Session>(xml); - }); - - xml.node("resource", [&] () { - xml.attribute("name", "RAM"); - xml.attribute("preserve", "1M"); - }); - - xml.node("start", [&] () { - _gen_menu_view_start_content(xml, "menu", Point(0, 0), menu_width); }); - - xml.node("start", [&] () { - _gen_menu_view_start_content(xml, "popup", Point(0, 0), 0); }); -} - diff --git a/repos/gems/src/app/sculpt_manager/gui.h b/repos/gems/src/app/sculpt_manager/gui.h deleted file mode 100644 index b55df6aa1..000000000 --- a/repos/gems/src/app/sculpt_manager/gui.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * \brief Sculpt GUI management - * \author Norman Feske - * \date 2018-04-30 - * - * The GUI is based on a dynamically configured init component, which hosts - * one menu-view component for each dialog. - */ - -/* - * 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 _GUI_H_ -#define _GUI_H_ - -/* Genode includes */ -#include - -/* local includes */ -#include -#include - -namespace Sculpt { struct Gui; } - - -struct Sculpt::Gui -{ - Env &_env; - - Expanding_reporter _config { _env, "config", "gui_config" }; - - float _font_size_px = 14; - - typedef String<32> Label; - - struct Version { unsigned value; } version { 0 }; - - unsigned menu_width = 0; - - void _gen_menu_view_start_content(Xml_generator &, Label const &, Point, - unsigned) const; - - void _generate_config(Xml_generator &) const; - - void generate_config() - { - _config.generate([&] (Xml_generator &xml) { _generate_config(xml); }); - } - - float font_size() const { return _font_size_px; } - - void font_size(float px) - { - _font_size_px = px; - menu_width = max(px*21, 320.0); - } - - Gui(Env &env) : _env(env) { } -}; - -#endif /* _GUI_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/keyboard_focus.h b/repos/gems/src/app/sculpt_manager/keyboard_focus.h index de6315138..183e899bb 100644 --- a/repos/gems/src/app/sculpt_manager/keyboard_focus.h +++ b/repos/gems/src/app/sculpt_manager/keyboard_focus.h @@ -20,6 +20,7 @@ /* local includes */ #include #include +#include namespace Sculpt { struct Keyboard_focus; } @@ -29,8 +30,9 @@ struct Sculpt::Keyboard_focus Expanding_reporter _focus_reporter; - Network_dialog const &_network_dialog; - Wpa_passphrase &_wpa_passphrase; + Network_dialog const &_network_dialog; + Wpa_passphrase &_wpa_passphrase; + Panel_dialog::State const &_panel; void update() { @@ -38,7 +40,7 @@ struct Sculpt::Keyboard_focus target = WM; - if (_network_dialog.need_keyboard_focus_for_passphrase()) + if (_panel.network_visible() && _network_dialog.need_keyboard_focus_for_passphrase()) target = WPA_PASSPHRASE; if (orig_target == target) @@ -63,12 +65,14 @@ struct Sculpt::Keyboard_focus } Keyboard_focus(Env &env, - Network_dialog const &network_dialog, - Wpa_passphrase &wpa_passphrase) + Network_dialog const &network_dialog, + Wpa_passphrase &wpa_passphrase, + Panel_dialog::State const &panel) : _focus_reporter(env, "focus", "focus"), _network_dialog(network_dialog), - _wpa_passphrase(wpa_passphrase) + _wpa_passphrase(wpa_passphrase), + _panel(panel) { update(); } diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index 67fbfe0b2..5b2ca4494 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -27,9 +28,13 @@ #include #include #include +#include #include #include -#include +#include +#include +#include +#include #include #include #include @@ -45,9 +50,15 @@ struct Sculpt::Main : Input_event_handler, Runtime_config_generator, Storage::Target_user, Graph::Action, + Panel_dialog::Action, Popup_dialog::Action, + Settings_dialog::Action, + File_browser_dialog::Action, Popup_dialog::Construction_info, - Depot_query + Depot_query, + Panel_dialog::State, + Dialog, + Popup_dialog::Refresh { Env &_env; @@ -55,6 +66,8 @@ struct Sculpt::Main : Input_event_handler, Sculpt_version const _sculpt_version { _env }; + Registry _child_states { }; + Constructible _nitpicker { }; Signal_handler
_input_handler { @@ -88,7 +101,7 @@ struct Sculpt::Main : Input_event_handler, type.for_each_sub_node("ttf", [&] (Xml_node ttf) { float const px = ttf.attribute_value("size_px", 0.0); if (px > 0.0) - _gui.font_size(px); }); } }); } }); }); + _font_size_px = px; }); } }); } }); }); _handle_nitpicker_mode(); } @@ -131,6 +144,8 @@ struct Sculpt::Main : Input_event_handler, if (device.attribute_value("class_code", 0UL) == 0x28000) _pci_info.wifi_present = true; }); + + _network.update_view(); } @@ -147,7 +162,7 @@ struct Sculpt::Main : Input_event_handler, } - Storage _storage { _env, _heap, *this, *this, *this }; + Storage _storage { _env, _heap, _child_states, *this, *this, *this }; /** * Storage::Target_user interface @@ -165,7 +180,7 @@ struct Sculpt::Main : Input_event_handler, } - Network _network { _env, _heap, *this, *this, _runtime_state, _pci_info }; + Network _network { _env, _heap, _child_states, *this, _runtime_state, _pci_info }; /************ @@ -307,12 +322,12 @@ struct Sculpt::Main : Input_event_handler, _launchers.update_from_xml(dir); } - _popup_dialog.generate(); + _popup_menu_view.generate(); _deploy._handle_managed_deploy(); } - Deploy _deploy { _env, _heap, _runtime_state, *this, *this, *this, + Deploy _deploy { _env, _heap, _child_states, _runtime_state, *this, *this, *this, _launcher_listing_rom, _blueprint_rom, _download_queue }; Attached_rom_dataspace _manual_deploy_rom { _env, "config -> deploy" }; @@ -332,102 +347,103 @@ struct Sculpt::Main : Input_event_handler, ** Global ** ************/ - Gui _gui { _env }; + Font_size _font_size = Font_size::MEDIUM; - Expanding_reporter _menu_dialog_reporter { _env, "dialog", "menu_dialog" }; + float _font_size_px = 14; - Attached_rom_dataspace _hover_rom { _env, "menu_view_hover" }; + Area _screen_size { }; - Signal_handler
_hover_handler { - _env.ep(), *this, &Main::_handle_hover }; + Panel_dialog::Tab _selected_tab = Panel_dialog::Tab::COMPONENTS; - struct Hovered { enum Dialog { NONE, LOGO, STORAGE, NETWORK, RUNTIME } value; }; + bool _log_visible = false; + bool _network_visible = false; + bool _settings_visible = false; - Hovered::Dialog _hovered_dialog { Hovered::NONE }; + File_browser_state _file_browser_state { }; - template - void _apply_to_hovered_dialog(Hovered::Dialog dialog, FN const &fn) + Attached_rom_dataspace _editor_saved_rom { _env, "report -> runtime/editor/saved" }; + + + /** + * 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 inspect_tab_visible() const override { return _storage.any_file_system_inspected(); } + + Panel_dialog::Tab selected_tab() const override { return _selected_tab; } + + /** + * Dialog interface + */ + Hover_result hover(Xml_node) override; + + void reset() override { } + + /** + * Dialog interface + */ + void generate(Xml_generator &xml) const override { - if (dialog == Hovered::STORAGE) fn(_storage.dialog); - if (dialog == Hovered::NETWORK) fn(_network.dialog); - } + xml.node("vbox", [&] () { + if (_manually_managed_runtime) + return; - void _handle_hover(); + bool const network_missing = _deploy.update_needed() + && !_network._nic_state.ready(); + bool const show_diagnostics = + _deploy.any_unsatisfied_child() || network_missing; + + auto gen_network_diagnostics = [&] (Xml_generator &xml) + { + if (!network_missing) + return; + + gen_named_node(xml, "hbox", "network", [&] () { + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", "yes"); + xml.node("label", [&] () { + xml.attribute("text", "network needed for installation"); + xml.attribute("font", "annotation/regular"); + }); + }); + }); + }; + + if (show_diagnostics) { + gen_named_node(xml, "frame", "diagnostics", [&] () { + xml.node("vbox", [&] () { + + xml.node("label", [&] () { + xml.attribute("text", "Diagnostics"); }); + + xml.node("float", [&] () { + xml.node("vbox", [&] () { + gen_network_diagnostics(xml); + _deploy.gen_child_diagnostics(xml); + }); + }); + }); + }); + } + + Xml_node const state = _update_state_rom.xml(); + if (_update_running() && state.attribute_value("progress", false)) + gen_download_status(xml, state); + }); + } /** * Dialog::Generator interface */ void generate_dialog() override { - _menu_dialog_reporter.generate([&] (Xml_generator &xml) { - - xml.node("vbox", [&] () { - gen_named_node(xml, "frame", "logo", [&] () { - xml.node("float", [&] () { - xml.node("frame", [&] () { - xml.attribute("style", "logo"); }); }); }); - - if (_manually_managed_runtime) - return; - - bool const storage_dialog_expanded = _last_clicked == Hovered::STORAGE - || !_storage.any_file_system_inspected(); - - _storage.dialog.generate(xml, storage_dialog_expanded); - _network.dialog.generate(xml); - - gen_named_node(xml, "frame", "runtime", [&] () { - xml.node("vbox", [&] () { - gen_named_node(xml, "label", "title", [&] () { - xml.attribute("text", "Runtime"); - xml.attribute("font", "title/regular"); - }); - - bool const network_missing = _deploy.update_needed() - && !_network._nic_state.ready(); - bool const show_diagnostics = - _deploy.any_unsatisfied_child() || network_missing; - - auto gen_network_diagnostics = [&] (Xml_generator &xml) - { - if (!network_missing) - return; - - gen_named_node(xml, "hbox", "network", [&] () { - gen_named_node(xml, "float", "left", [&] () { - xml.attribute("west", "yes"); - xml.node("label", [&] () { - xml.attribute("text", "network needed for installation"); - xml.attribute("font", "annotation/regular"); - }); - }); - }); - }; - - if (show_diagnostics) { - gen_named_node(xml, "frame", "diagnostics", [&] () { - xml.node("vbox", [&] () { - - xml.node("label", [&] () { - xml.attribute("text", "Diagnostics"); }); - - xml.node("float", [&] () { - xml.node("vbox", [&] () { - gen_network_diagnostics(xml); - _deploy.gen_child_diagnostics(xml); - }); - }); - }); - }); - } - - Xml_node const state = _update_state_rom.xml(); - if (_update_running() && state.attribute_value("progress", false)) - gen_download_status(xml, state); - }); - }); - }); - }); + _main_menu_view.generate(); + _graph_menu_view.generate(); } Attached_rom_dataspace _runtime_state_rom { _env, "report -> runtime/state" }; @@ -485,7 +501,10 @@ struct Sculpt::Main : Input_event_handler, { _runtime_config_rom.update(); _cached_runtime_config.update_from_xml(_runtime_config_rom.xml()); - _graph.gen_dialog(); + _graph_menu_view.generate(); + + if (_selected_tab == Panel_dialog::Tab::FILES) + _file_browser_menu_view.generate(); } @@ -493,9 +512,7 @@ struct Sculpt::Main : Input_event_handler, ** Interactive operations ** ****************************/ - Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase }; - - Hovered::Dialog _last_clicked { Hovered::NONE }; + Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase, *this }; /** * Input_event_handler interface @@ -506,18 +523,8 @@ struct Sculpt::Main : Input_event_handler, if (ev.key_press(Input::BTN_LEFT)) { - if (_hovered_dialog != _last_clicked && _hovered_dialog != Hovered::NONE) { - _last_clicked = _hovered_dialog; - _handle_window_layout(); - need_generate_dialog = true; - } - - if (_hovered_dialog == Hovered::STORAGE) _storage.dialog.click(_storage); - if (_hovered_dialog == Hovered::NETWORK) _network.dialog.click(_network); - if (_hovered_dialog == Hovered::RUNTIME) _network.dialog.click(_network); - /* remove popup dialog when clicking somewhere outside */ - if (!_popup_dialog.hovered() && _popup.state == Popup::VISIBLE + if (!_popup_menu_view.hovered() && _popup.state == Popup::VISIBLE && !_graph.add_button_hovered()) { _popup.state = Popup::OFF; @@ -525,21 +532,50 @@ struct Sculpt::Main : Input_event_handler, discard_construction(); /* de-select '+' button */ - _graph._gen_graph_dialog(); + _graph_menu_view.generate(); /* remove popup window from window layout */ _handle_window_layout(); } - if (_graph.hovered()) _graph.click(*this); - if (_popup_dialog.hovered()) _popup_dialog.click(*this); + if (_main_menu_view.hovered()) { + _main_menu_view.generate(); + } + else if (_graph_menu_view.hovered()) { + _graph.click(*this); + _graph_menu_view.generate(); + } + else if (_popup_menu_view.hovered()) { + _popup_dialog.click(*this); + _popup_menu_view.generate(); + } + else if (_panel_menu_view.hovered()) { + _panel_dialog.click(*this); + } + else if (_settings_menu_view.hovered()) { + _settings_dialog.click(*this); + _settings_menu_view.generate(); + } + else if (_network.dialog_hovered()) { + _network.dialog.click(_network); + _network.update_view(); + } + else if (_file_browser_menu_view.hovered()) { + _file_browser_dialog.click(*this); + _file_browser_menu_view.generate(); + } } if (ev.key_release(Input::BTN_LEFT)) { - if (_hovered_dialog == Hovered::STORAGE) _storage.dialog.clack(_storage); - - if (_graph.hovered()) _graph.clack(*this); - if (_popup_dialog.hovered()) _popup_dialog.clack(*this); + if (_main_menu_view.hovered()) { + _storage.dialog.clack(_storage); + _main_menu_view.generate(); + } + else if (_graph_menu_view.hovered()) { + _graph.clack(*this, _storage); + _graph_menu_view.generate(); + } + else if (_popup_menu_view.hovered()) _popup_dialog.clack(*this); } if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE) @@ -553,6 +589,52 @@ struct Sculpt::Main : Input_event_handler, generate_dialog(); } + /* + * Fs_dialog::Action interface + */ + void toggle_file_browser(Storage_target const &target) override + { + _storage.toggle_file_browser(target); + + /* refresh visibility to inspect tab */ + _panel_menu_view.generate(); + } + + void use(Storage_target const &target) override { _storage.use(target); } + + /* + * Storage_dialog::Action interface + */ + void format(Storage_target const &target) override + { + _storage.format(target); + } + + void cancel_format(Storage_target const &target) override + { + _storage.cancel_format(target); + } + + void expand(Storage_target const &target) override + { + _storage.expand(target); + } + + void cancel_expand(Storage_target const &target) override + { + _storage.cancel_expand(target); + } + + void check(Storage_target const &target) override + { + _storage.check(target); + } + + void toggle_default_storage_target(Storage_target const &target) override + { + _storage.toggle_default_storage_target(target); + } + /* * Graph::Action interface */ @@ -569,13 +651,214 @@ struct Sculpt::Main : Input_event_handler, */ void toggle_launcher_selector(Rect anchor) override { - _popup_dialog.generate(); + _popup_menu_view.generate(); _popup.anchor = anchor; _popup.toggle(); - _graph._gen_graph_dialog(); + _graph_menu_view.generate(); _handle_window_layout(); } + void _refresh_panel_and_window_layout() + { + _panel_menu_view.generate(); + _handle_window_layout(); + } + + /* + * Panel::Action interface + */ + void select_tab(Panel_dialog::Tab tab) override + { + _selected_tab = tab; + + if (_selected_tab == Panel_dialog::Tab::FILES) + _file_browser_menu_view.generate(); + + _refresh_panel_and_window_layout(); + } + + /* + * Panel::Action interface + */ + void toggle_log_visibility() override + { + _log_visible = !_log_visible; + _refresh_panel_and_window_layout(); + } + + /* + * Panel::Action interface + */ + void toggle_network_visibility() override + { + _network_visible = !_network_visible; + _refresh_panel_and_window_layout(); + } + + /* + * Panel::Action interface + */ + void toggle_settings_visibility() override + { + _settings_visible = !_settings_visible; + _refresh_panel_and_window_layout(); + } + + /* + * Settings_dialog::Action interface + */ + void select_font_size(Font_size font_size) override + { + _font_size = font_size; + _handle_nitpicker_mode(); + } + + Signal_handler
_fs_query_result_handler { + _env.ep(), *this, &Main::_handle_fs_query_result }; + + void _handle_fs_query_result() + { + _file_browser_state.update_query_results(); + _file_browser_menu_view.generate(); + } + + Signal_handler
_editor_saved_handler { + _env.ep(), *this, &Main::_handle_editor_saved }; + + void _handle_editor_saved() + { + _editor_saved_rom.update(); + + Xml_node const saved = _editor_saved_rom.xml(); + + bool const orig_modified = _file_browser_state.modified; + + _file_browser_state.modified = saved.attribute_value("modified", false); + _file_browser_state.last_saved_version = saved.attribute_value("version", 0U); + + if (orig_modified != _file_browser_state.modified) + _file_browser_menu_view.generate(); + } + + void _close_edited_file() + { + _file_browser_state.edited_file = File_browser_state::File(); + _file_browser_state.text_area.destruct(); + _file_browser_state.edit = false; + } + + /* + * File_browser_dialog::Action interface + */ + void browse_file_system(File_browser_state::Fs_name const &name) override + { + using Fs_name = File_browser_state::Fs_name; + + _close_edited_file(); + + if (name == _file_browser_state.browsed_fs) { + _file_browser_state.browsed_fs = Fs_name(); + _file_browser_state.fs_query.destruct(); + + } else { + _file_browser_state.browsed_fs = name; + _file_browser_state.path = File_browser_state::Path("/"); + + Start_name const start_name(name, ".query"); + _file_browser_state.fs_query.construct(_child_states, start_name, + Ram_quota{8*1024*1024}, Cap_quota{200}); + + 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); + _handle_fs_query_result(); + } + + generate_runtime_config(); + + _file_browser_menu_view.generate(); + } + + void browse_sub_directory(File_browser_state::Sub_dir const &sub_dir) override + { + _close_edited_file(); + + if (_file_browser_state.path == "/") + _file_browser_state.path = + File_browser_state::Path("/", sub_dir); + else + _file_browser_state.path = + File_browser_state::Path(_file_browser_state.path, "/", sub_dir); + + generate_runtime_config(); + } + + void browse_parent_directory() override + { + _close_edited_file(); + + Genode::Path<256> path(_file_browser_state.path); + path.strip_last_element(); + _file_browser_state.path = File_browser_state::Path(path); + + generate_runtime_config(); + } + + void browse_abs_directory(File_browser_state::Path const &path) override + { + _close_edited_file(); + + _file_browser_state.path = path; + + generate_runtime_config(); + } + + void _view_or_edit_file(File_browser_state::File const &file, bool edit) + { + if (_file_browser_state.edited_file == file) { + _close_edited_file(); + } else { + _file_browser_state.edited_file = file; + _file_browser_state.edit = edit; + _file_browser_state.save_version = 0; + + if (_file_browser_state.text_area.constructed()) { + _file_browser_state.text_area->trigger_restart(); + } else { + Start_name const start_name("editor"); + _file_browser_state.text_area.construct(_child_states, start_name, + Ram_quota{16*1024*1024}, Cap_quota{250}); + } + } + + generate_runtime_config(); + } + + void view_file(File_browser_state::File const &file) override + { + _view_or_edit_file(file, false); + } + + void edit_file(File_browser_state::File const &file) override + { + _view_or_edit_file(file, true); + } + + void revert_edited_file() override + { + if (_file_browser_state.text_area.constructed()) + _file_browser_state.text_area->trigger_restart(); + + generate_runtime_config(); + } + + void save_edited_file() override + { + _file_browser_state.save_version = _file_browser_state.last_saved_version + 1; + generate_runtime_config(); + } + void _close_popup_dialog() { /* close popup menu */ @@ -584,7 +867,7 @@ struct Sculpt::Main : Input_event_handler, _handle_window_layout(); /* reset state of the '+' button */ - _graph._gen_graph_dialog(); + _graph_menu_view.generate(); } /* @@ -656,11 +939,42 @@ struct Sculpt::Main : Input_event_handler, _runtime_state.with_construction([&] (Component const &c) { fn.with(c); }); } - Popup_dialog _popup_dialog { _env, _heap, _launchers, + Panel_dialog _panel_dialog { *this }; + + Menu_view _panel_menu_view { _env, _child_states, _panel_dialog, "panel_view", + Ram_quota{4*1024*1024}, Cap_quota{150}, + "panel_dialog", "panel_view_hover" }; + + Settings_dialog _settings_dialog { _font_size }; + + 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" }; + + Menu_view _main_menu_view { _env, _child_states, *this, "menu_view", + Ram_quota{4*1024*1024}, Cap_quota{150}, + "menu_dialog", "menu_view_hover" }; + + Popup_dialog _popup_dialog { _env, *this, _launchers, _network._nic_state, _network._nic_target, _runtime_state, _cached_runtime_config, _download_queue, *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" }; + + 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" }; + + /** + * Popup_dialog::Refresh interface + */ + void refresh_popup_dialog() override { _popup_menu_view.generate(); } + Managed_config
_fb_drv_config { _env, "config", "fb_drv", *this, &Main::_handle_fb_drv_config }; @@ -704,7 +1018,7 @@ struct Sculpt::Main : Input_event_handler, */ static Nitpicker::Root gui_nitpicker(_env, _heap, *this); - _gui.generate_config(); + generate_runtime_config(); } void _handle_window_layout(); @@ -738,13 +1052,13 @@ struct Sculpt::Main : Input_event_handler, Popup _popup { }; - Graph _graph { _env, _runtime_state, _cached_runtime_config, - _storage._sculpt_partition, _popup.state, _deploy._children }; - - Child_state _runtime_view_state { - "runtime_view", Ram_quota{8*1024*1024}, Cap_quota{200} }; - + Graph _graph { _runtime_state, _cached_runtime_config, _storage._storage_devices, + _storage._sculpt_partition, _storage._ram_fs_state, + _popup.state, _deploy._children }; + Menu_view _graph_menu_view { _env, _child_states, _graph, "runtime_view", + Ram_quota{8*1024*1024}, Cap_quota{200}, + "runtime_dialog", "runtime_view_hover" }; Main(Env &env) : _env(env) { _manual_deploy_rom.sigh(_manual_deploy_handler); @@ -757,12 +1071,12 @@ struct Sculpt::Main : Input_event_handler, */ _update_state_rom .sigh(_update_state_handler); _nitpicker_hover .sigh(_nitpicker_hover_handler); - _hover_rom .sigh(_hover_handler); _pci_devices .sigh(_pci_devices_handler); _window_list .sigh(_window_list_handler); _decorator_margins .sigh(_decorator_margins_handler); _launcher_listing_rom.sigh(_launcher_listing_handler); _blueprint_rom .sigh(_blueprint_handler); + _editor_saved_rom .sigh(_editor_saved_handler); /* * Generate initial configurations @@ -812,56 +1126,64 @@ void Sculpt::Main::_handle_window_layout() _decorator_margins.update(); Decorator_margins const margins(_decorator_margins.xml()); - unsigned const log_min_w = 400, log_min_h = 200; + unsigned const log_min_w = 400; if (!_nitpicker.constructed()) return; + typedef String<128> Label; + Label const + inspect_label ("runtime -> leitzentrale -> inspect"), + runtime_view_label ("runtime -> leitzentrale -> runtime_view"), + panel_view_label ("runtime -> leitzentrale -> panel_view"), + menu_view_label ("runtime -> leitzentrale -> menu_view"), + popup_view_label ("runtime -> leitzentrale -> popup_view"), + settings_view_label ("runtime -> leitzentrale -> settings_view"), + network_view_label ("runtime -> leitzentrale -> network_view"), + file_browser_view_label("runtime -> leitzentrale -> file_browser_view"), + editor_view_label ("runtime -> leitzentrale -> editor"), + logo_label ("logo"); + + _window_list.update(); + Xml_node const window_list = _window_list.xml(); + + auto win_size = [&] (Xml_node win) { + return Area(win.attribute_value("width", 0UL), + win.attribute_value("height", 0UL)); }; + + unsigned panel_height = 0; + _with_window(window_list, panel_view_label, [&] (Xml_node win) { + panel_height = win_size(win).h(); }); + + /* suppress intermediate states during the restart of the panel */ + if (panel_height == 0) + return; + Framebuffer::Mode const mode = _nitpicker->mode(); - /* area preserved for the menu */ - Rect const menu(Point(0, 0), Area(_gui.menu_width, mode.height())); + /* area reserved for the panel */ + Rect const panel(Point(0, 0), Area(mode.width(), panel_height)); /* available space on the right of the menu */ - Rect avail(Point(_gui.menu_width, 0), + Rect avail(Point(0, panel.h()), Point(mode.width() - 1, mode.height() - 1)); - /* - * When the screen width is at least twice the log width, place the - * log at the right side of the screen. Otherwise, with resolutions - * as low as 1024x768, place it to the bottom to allow the inspect - * window to use the available screen width to the maximum extend. - */ - bool const log_at_right = - (avail.w() > 2*(log_min_w + margins.left + margins.right)); + Point const log_offset = _log_visible + ? Point(0, 0) + : Point(log_min_w + margins.left + margins.right, 0); - /* the upper-left point depends on whether the log is at the right or bottom */ - Point const log_p1 = - log_at_right ? Point(avail.x2() - log_min_w - margins.right + 1, - margins.top) - : Point(_gui.menu_width + margins.left, - avail.y2() - log_min_h - margins.bottom + 1); - - /* the lower-right point (p2) of the log is always the same */ - Point const log_p2(mode.width() - margins.right - 1, + Point const log_p1(avail.x2() - log_min_w - margins.right + 1 + log_offset.x(), + avail.y1() + margins.top); + Point const log_p2(mode.width() - margins.right - 1 + log_offset.x(), mode.height() - margins.bottom - 1); /* position of the inspect window */ - Point const inspect_p1(avail.x1() + margins.right, margins.top); + Point const inspect_p1(avail.x1() + margins.left, avail.y1() + margins.top); + Point const inspect_p2(avail.x2() - margins.right - 1, + avail.y2() - margins.bottom - 1); - Point const inspect_p2 = - log_at_right ? Point(log_p1.x() - margins.right - margins.left - 1, log_p2.y()) - : Point(log_p2.x(), log_p1.y() - margins.bottom - margins.top - 1); - - typedef String<128> Label; - Label const inspect_label ("runtime -> leitzentrale -> inspect"); - Label const runtime_view_label("runtime -> leitzentrale -> runtime_view"); - - _window_list.update(); _window_layout.generate([&] (Xml_generator &xml) { - Xml_node const window_list = _window_list.xml(); - auto gen_window = [&] (Xml_node win, Rect rect) { if (rect.valid()) { xml.node("window", [&] () { @@ -875,10 +1197,6 @@ void Sculpt::Main::_handle_window_layout() } }; - auto win_size = [&] (Xml_node win) { - return Area(win.attribute_value("width", 0UL), - win.attribute_value("height", 0UL)); }; - /* window size limited to space unobstructed by the menu and log */ auto constrained_win_size = [&] (Xml_node win) { @@ -889,8 +1207,62 @@ void Sculpt::Main::_handle_window_layout() return Area(min(inspect_w, size.w()), min(inspect_h, size.h())); }; - _with_window(window_list, Label("gui -> menu -> "), [&] (Xml_node win) { - gen_window(win, menu); }); + _with_window(window_list, panel_view_label, [&] (Xml_node win) { + gen_window(win, panel); }); + + _with_window(window_list, Label("log"), [&] (Xml_node win) { + gen_window(win, Rect(log_p1, log_p2)); }); + + _with_window(window_list, settings_view_label, [&] (Xml_node win) { + Area const size = win_size(win); + Point const pos = _settings_visible + ? Point(0, avail.y1()) + : Point(-size.w(), avail.y1()); + gen_window(win, Rect(pos, size)); + }); + + _with_window(window_list, network_view_label, [&] (Xml_node win) { + Area const size = win_size(win); + Point const pos = _network_visible + ? Point(log_p1.x() - size.w(), avail.y1()) + : Point(mode.width(), avail.y1()); + gen_window(win, Rect(pos, size)); + }); + + _with_window(window_list, file_browser_view_label, [&] (Xml_node win) { + if (_selected_tab == Panel_dialog::Tab::FILES) { + + Area const size = constrained_win_size(win); + Point const pos = Rect(inspect_p1, inspect_p2).center(size); + + Point const offset = _file_browser_state.text_area.constructed() + ? Point((2*avail.w())/3 - pos.x(), 0) + : Point(0, 0); + + gen_window(win, Rect(pos - offset, size)); + } + }); + + _with_window(window_list, editor_view_label, [&] (Xml_node win) { + if (_selected_tab == Panel_dialog::Tab::FILES) { + Area const size = constrained_win_size(win); + Point const pos = Rect(inspect_p1 + Point(400, 0), inspect_p2).center(size); + + Point const offset = _file_browser_state.text_area.constructed() + ? Point(avail.w()/3 - pos.x(), 0) + : Point(0, 0); + + gen_window(win, Rect(pos + offset, size)); + } + }); + + _with_window(window_list, menu_view_label, [&] (Xml_node win) { + if (_selected_tab == Panel_dialog::Tab::COMPONENTS) { + Area const size = win_size(win); + Point const pos(0, avail.y2() - size.h()); + gen_window(win, Rect(pos, size)); + } + }); /* * Calculate centered runtime view within the available main (inspect) @@ -903,20 +1275,20 @@ void Sculpt::Main::_handle_window_layout() }); if (_popup.state == Popup::VISIBLE) { - _with_window(window_list, Label("gui -> popup -> "), [&] (Xml_node win) { + _with_window(window_list, popup_view_label, [&] (Xml_node win) { Area const size = win_size(win); int const anchor_y_center = (_popup.anchor.y1() + _popup.anchor.y2())/2; int const x = runtime_view_pos.x() + _popup.anchor.x2(); - int const y = max(0, runtime_view_pos.y() + anchor_y_center - (int)size.h()/2); + int const y = max((int)panel_height, runtime_view_pos.y() + anchor_y_center - (int)size.h()/2); gen_window(win, Rect(Point(x, y), size)); }); } - if (_last_clicked == Hovered::STORAGE) - _with_window(window_list, inspect_label, [&] (Xml_node win) { + _with_window(window_list, inspect_label, [&] (Xml_node win) { + if (_selected_tab == Panel_dialog::Tab::INSPECT) gen_window(win, Rect(inspect_p1, inspect_p2)); }); /* @@ -924,17 +1296,26 @@ void Sculpt::Main::_handle_window_layout() * the overlapping of the log area. (use the menu view's 'win_size'). */ _with_window(window_list, runtime_view_label, [&] (Xml_node win) { - gen_window(win, Rect(runtime_view_pos, win_size(win))); }); + if (_selected_tab == Panel_dialog::Tab::COMPONENTS) + gen_window(win, Rect(runtime_view_pos, win_size(win))); }); - _with_window(window_list, Label("log"), [&] (Xml_node win) { - gen_window(win, Rect(log_p1, log_p2)); }); + _with_window(window_list, logo_label, [&] (Xml_node win) { + Area const size = win_size(win); + Point const pos(mode.width() - size.w(), mode.height() - size.h()); + gen_window(win, Rect(pos, size)); + }); }); /* define window-manager focus */ _wm_focus.generate([&] (Xml_generator &xml) { _window_list.xml().for_each_sub_node("window", [&] (Xml_node win) { Label const label = win.attribute_value("label", Label()); - if (label == inspect_label) + + if (label == inspect_label && _selected_tab == Panel_dialog::Tab::INSPECT) + xml.node("window", [&] () { + xml.attribute("id", win.attribute_value("id", 0UL)); }); + + if (label == editor_view_label && _selected_tab == Panel_dialog::Tab::FILES) xml.node("window", [&] () { xml.attribute("id", win.attribute_value("id", 0UL)); }); }); @@ -953,9 +1334,17 @@ void Sculpt::Main::_handle_nitpicker_mode() if (!_fonts_config.try_generate_manually_managed()) { - float const text_size = (float)mode.height() / 60.0; + _font_size_px = (float)mode.height() / 60.0; - _gui.font_size(text_size); + if (_font_size == Font_size::SMALL) _font_size_px *= 0.85; + if (_font_size == Font_size::LARGE) _font_size_px *= 1.35; + + Area const size(mode.width(), mode.height()); + _screen_size = size; + _panel_menu_view.min_width = size.w(); + unsigned const menu_width = max(_font_size_px*21, 320.0); + _main_menu_view.min_width = menu_width; + _network.min_dialog_width(menu_width); _fonts_config.generate([&] (Xml_generator &xml) { xml.attribute("copy", true); @@ -977,10 +1366,10 @@ void Sculpt::Main::_handle_nitpicker_mode() }); }; - gen_ttf_dir("title", "/Vera.ttf", text_size*1.25); - gen_ttf_dir("text", "/Vera.ttf", text_size); - gen_ttf_dir("annotation", "/Vera.ttf", text_size*0.8); - gen_ttf_dir("monospace", "/VeraMono.ttf", text_size); + gen_ttf_dir("title", "/Vera.ttf", _font_size_px*1.25); + gen_ttf_dir("text", "/Vera.ttf", _font_size_px); + gen_ttf_dir("annotation", "/Vera.ttf", _font_size_px*0.8); + gen_ttf_dir("monospace", "/VeraMono.ttf", _font_size_px); }); }); xml.node("default-policy", [&] () { xml.attribute("root", "/fonts"); }); @@ -1001,35 +1390,22 @@ void Sculpt::Main::_handle_nitpicker_mode() }); } - _gui.version.value++; - _gui.generate_config(); + /* font size may has changed */ + _panel_menu_view.trigger_restart(); + _main_menu_view.trigger_restart(); + _file_browser_menu_view.trigger_restart(); + _network.trigger_dialog_restart(); + _graph_menu_view.trigger_restart(); + _popup_menu_view.trigger_restart(); + _settings_menu_view.trigger_restart(); + + generate_runtime_config(); } -void Sculpt::Main::_handle_hover() +Sculpt::Dialog::Hover_result Sculpt::Main::hover(Xml_node hover) { - _hover_rom.update(); - Xml_node const hover = _hover_rom.xml(); - - Hovered::Dialog const orig_hovered_dialog = _hovered_dialog; - - typedef String<32> Top_level_frame; - Top_level_frame const top_level_frame = - query_attribute(hover, "dialog", "vbox", "frame", "name"); - - _hovered_dialog = Hovered::NONE; - if (top_level_frame == "network") _hovered_dialog = Hovered::NETWORK; - if (top_level_frame == "storage") _hovered_dialog = Hovered::STORAGE; - if (top_level_frame == "runtime") _hovered_dialog = Hovered::RUNTIME; - if (top_level_frame == "logo") _hovered_dialog = Hovered::LOGO; - - if (orig_hovered_dialog != _hovered_dialog) - _apply_to_hovered_dialog(orig_hovered_dialog, [&] (Dialog &dialog) { - dialog.hover(Xml_node("")); }); - - _apply_to_hovered_dialog(_hovered_dialog, [&] (Dialog &dialog) { - dialog.hover(hover.sub_node("dialog").sub_node("vbox") - .sub_node("frame")); }); + return _storage.dialog.match_sub_dialog(hover, "vbox", "frame", "vbox"); } @@ -1122,6 +1498,7 @@ void Sculpt::Main::_handle_runtime_state() partition.check_in_progress = 0; reconfigure_runtime = true; _storage.dialog.reset_operation(); + _graph.reset_operation(); } } @@ -1141,6 +1518,7 @@ void Sculpt::Main::_handle_runtime_state() reconfigure_runtime = true; _storage.dialog.reset_operation(); + _graph.reset_operation(); } } @@ -1152,6 +1530,7 @@ void Sculpt::Main::_handle_runtime_state() reconfigure_runtime = true; device.rediscover(); _storage.dialog.reset_operation(); + _graph.reset_operation(); } } @@ -1164,6 +1543,7 @@ void Sculpt::Main::_handle_runtime_state() device.rediscover(); reconfigure_runtime = true; _storage.dialog.reset_operation(); + _graph.reset_operation(); } } @@ -1182,6 +1562,7 @@ void Sculpt::Main::_handle_runtime_state() reconfigure_runtime = true; _storage.dialog.reset_operation(); + _graph.reset_operation(); } } @@ -1239,12 +1620,12 @@ void Sculpt::Main::_handle_runtime_state() /* upgrade RAM and cap quota on demand */ state.for_each_sub_node("child", [&] (Xml_node child) { - /* use binary OR (|), not logical OR (||), to always execute all elements */ - if (_storage._ram_fs_state.apply_child_state_report(child) - | _deploy.cached_depot_rom_state.apply_child_state_report(child) - | _deploy.uncached_depot_rom_state.apply_child_state_report(child) - | _runtime_view_state.apply_child_state_report(child)) { + bool reconfiguration_needed = false; + _child_states.for_each([&] (Child_state &child_state) { + if (child_state.apply_child_state_report(child)) + reconfiguration_needed = true; }); + if (reconfiguration_needed) { reconfigure_runtime = true; regenerate_dialog = true; } @@ -1261,8 +1642,10 @@ void Sculpt::Main::_handle_runtime_state() regenerate_dialog = true; } - if (regenerate_dialog) + if (regenerate_dialog) { generate_dialog(); + _graph_menu_view.generate(); + } if (reconfigure_runtime) generate_runtime_config(); @@ -1311,9 +1694,17 @@ void Sculpt::Main::_generate_runtime_config(Xml_generator &xml) const }); xml.node("start", [&] () { - gen_runtime_view_start_content(xml, _runtime_view_state, _gui.font_size()); }); + gen_runtime_view_start_content(xml, _graph_menu_view._child_state, _font_size_px); }); + + _panel_menu_view.gen_start_node(xml); + _main_menu_view.gen_start_node(xml); + _settings_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); /* * Load configuration and update depot config on the sculpt partition diff --git a/repos/gems/src/app/sculpt_manager/menu_view.cc b/repos/gems/src/app/sculpt_manager/menu_view.cc new file mode 100644 index 000000000..7f5ae8d15 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/menu_view.cc @@ -0,0 +1,152 @@ +/* + * \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 + +using namespace Sculpt; + + +void Menu_view::_handle_hover() +{ + using Hover_result = Dialog::Hover_result; + + _hover_rom.update(); + + bool const orig_hovered = _hovered; + + _hovered = false; + + Hover_result hover_result = Hover_result::UNMODIFIED; + + _hover_rom.xml().with_sub_node("dialog", [&] (Xml_node hover) { + _hovered = true; + hover_result = _dialog.hover(hover); + }); + + if (!_hovered) + _dialog.hover(Xml_node("")); + + bool const dialog_hover_changed = (_hovered != orig_hovered), + widget_hover_changed = (hover_result == Hover_result::CHANGED); + + if (dialog_hover_changed || widget_hover_changed) + generate(); +} + + +Menu_view::Menu_view(Env &env, Registry ®istry, + 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) +: + _dialog(dialog), + _child_state(registry, name, 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) +{ + _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("")); + _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, "binary", "menu_view"); + + xml.node("config", [&] () { + if (min_width) xml.attribute("width", min_width); + if (min_height) xml.attribute("height", min_height); + + 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 (xml); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + + using Label = String<128>; + + Label const label = _child_state.name(); + + gen_service_node(xml, [&] () { + xml.node("parent", [&] () { + xml.attribute("label", Label("leitzentrale -> ", label)); }); }); + + gen_service_node(xml, [&] () { + xml.attribute("label", "dialog"); + xml.node("parent", [&] () { + xml.attribute("label", Label("leitzentrale -> ", label, " -> dialog")); + }); + }); + + gen_service_node(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"); }); }); + }); +} diff --git a/repos/gems/src/app/sculpt_manager/menu_view.h b/repos/gems/src/app/sculpt_manager/menu_view.h new file mode 100644 index 000000000..b74383512 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/menu_view.h @@ -0,0 +1,72 @@ +/* + * \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 +#include +#include + +/* local includes */ +#include "types.h" +#include +#include + +namespace Sculpt { struct Menu_view; } + + +struct Sculpt::Menu_view : Noncopyable +{ + Dialog &_dialog; + + Child_state _child_state; + + Expanding_reporter _dialog_reporter; + + Attached_rom_dataspace _hover_rom; + + Signal_handler _hover_handler; + + bool _hovered = false; + + unsigned min_width = 0; + unsigned min_height = 0; + + void _handle_hover(); + + void _gen_start_node_content(Xml_generator &) const; + + Menu_view(Env &, Registry ®istry, + Dialog &, Start_name const &, Ram_quota, Cap_quota, + Session_label const &dialog_report_name, + Session_label const &hover_rom_name); + + void generate(); + + bool hovered() const { return _hovered; } + + 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_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/child_state.h b/repos/gems/src/app/sculpt_manager/model/child_state.h index 30a300388..50dd81980 100644 --- a/repos/gems/src/app/sculpt_manager/model/child_state.h +++ b/repos/gems/src/app/sculpt_manager/model/child_state.h @@ -16,6 +16,7 @@ /* Genode includes */ #include +#include /* local includes */ #include "types.h" @@ -26,6 +27,8 @@ struct Sculpt::Child_state : Noncopyable { private: + Registry::Element _element; + Start_name const _name; Ram_quota const _initial_ram_quota; @@ -44,9 +47,10 @@ struct Sculpt::Child_state : Noncopyable * \param ram_quota initial RAM quota * \param cap_quota initial capability quota */ - Child_state(Start_name const &name, + Child_state(Registry ®istry, Start_name const &name, Ram_quota ram_quota, Cap_quota cap_quota) : + _element(registry, *this), _name(name), _initial_ram_quota(ram_quota), _initial_cap_quota(cap_quota) { } @@ -58,12 +62,17 @@ struct Sculpt::Child_state : Noncopyable _cap_quota = _initial_cap_quota; } + void gen_start_node_version(Xml_generator &xml) const + { + if (_version.value) + xml.attribute("version", _version.value); + } + void gen_start_node_content(Xml_generator &xml) const { xml.attribute("name", _name); - if (_version.value) - xml.attribute("version", _version.value); + gen_start_node_version(xml); xml.attribute("caps", _cap_quota.value); gen_named_node(xml, "resource", "RAM", [&] () { @@ -102,6 +111,8 @@ struct Sculpt::Child_state : Noncopyable } Ram_quota ram_quota() const { return _ram_quota; } + + Start_name name() const { return _name; } }; #endif /* _MODEL__CHILD_STATE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/file_browser_state.h b/repos/gems/src/app/sculpt_manager/model/file_browser_state.h new file mode 100644 index 000000000..c094477a7 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/model/file_browser_state.h @@ -0,0 +1,217 @@ +/* + * \brief File_browser state + * \author Norman Feske + * \date 2020-01-31 + */ + +/* + * 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 _MODEL__FILE_BROWSER_STATE_H_ +#define _MODEL__FILE_BROWSER_STATE_H_ + +#include "types.h" +#include + +namespace Sculpt { struct File_browser_state; } + +struct Sculpt::File_browser_state : Noncopyable +{ + using Fs_name = Start_name; + using Path = String<256>; + using File = Path; + using Sub_dir = Path; + + Fs_name browsed_fs { }; + + Constructible fs_query { }; + Constructible text_area { }; + + Constructible query_result { }; + + File_browser_state() { }; + + Path path { }; + + /* + * File viewing and editing + */ + + File edited_file { }; + + bool edit = false; + bool modified = false; /* edited file has unsaved modifications */ + unsigned save_version = 0; /* version used for next save request */ + unsigned last_saved_version = 0; /* last version successfully saved */ + + void update_query_results() + { + if (query_result.constructed()) + query_result->update(); + } + + template + void with_query_result(FN const &fn) const + { + if (query_result.constructed()) + fn(query_result->xml()); + } + + using Index = Label; + + template + void with_entry_at_index(Index index, FN const &fn) const + { + unsigned cnt = 0; + with_query_result([&] (Xml_node node) { + node.with_sub_node("dir", [&] (Xml_node listing) { + listing.for_each_sub_node([&] (Xml_node entry) { + if (Index(cnt++) == index) + fn(entry); }); }); }); + } + + bool any_browsed_fs() const { return browsed_fs.length() > 0; } + + void gen_start_nodes(Xml_generator &xml) const + { + if (!fs_query.constructed() || !any_browsed_fs()) + return; + + xml.node("start", [&] () { + fs_query->gen_start_node_content(xml); + + gen_named_node(xml, "binary", "fs_query"); + + xml.node("config", [&] () { + xml.node("vfs", [&] () { + xml.node("fs", [&] () {}); }); + + xml.node("query", [&] () { + xml.attribute("path", path); + }); + }); + + xml.node("route", [&] () { + gen_parent_rom_route(xml, "fs_query"); + gen_parent_rom_route(xml, "ld.lib.so"); + gen_parent_rom_route(xml, "vfs.lib.so"); + + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + + gen_service_node<::File_system::Session>(xml, [&] () { + + if (browsed_fs == "config") { + xml.node("parent", [&] () { + xml.attribute("label", "config"); }); + } + else if (browsed_fs == "report") { + xml.node("parent", [&] () { + xml.attribute("label", "report"); }); + } + else { + xml.node("child", [&] () { + xml.attribute("name", browsed_fs); }); + } + }); + }); + }); + + if (edited_file.length() <= 1 || !text_area.constructed()) + return; + + xml.node("start", [&] () { + xml.attribute("name", text_area->name()); + + text_area->gen_start_node_version(xml); + + xml.attribute("caps", 350); + gen_named_node(xml, "resource", "RAM", [&] () { + xml.attribute("quantum", String<64>(22*1024*1024UL)); }); + + gen_named_node(xml, "binary", "text_area"); + + xml.node("config", [&] () { + Path const file_path = (path == "/") + ? Path("/", edited_file) + : Path(path, "/", edited_file); + xml.attribute("path", file_path); + xml.attribute("max_lines", 40); + xml.attribute("min_width", 600); + xml.attribute("copy", "yes"); + + if (edit) + xml.attribute("paste", "yes"); + else + xml.attribute("watch", "yes"); + + if (edit) { + xml.node("save", [&] () { + xml.attribute("version", save_version); }); + + xml.node("report", [&] () { + xml.attribute("saved", "yes"); }); + } + + xml.node("vfs", [&] () { + xml.node("fs", [&] () {}); }); + }); + + xml.node("route", [&] () { + gen_parent_rom_route(xml, "text_area"); + gen_parent_rom_route(xml, "ld.lib.so"); + gen_parent_rom_route(xml, "vfs.lib.so"); + gen_parent_rom_route(xml, "sandbox.lib.so"); + gen_parent_rom_route(xml, "menu_view"); + 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 (xml); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + gen_parent_route (xml); + + gen_service_node(xml, [&] () { + xml.attribute("label", "clipboard"); + xml.node("parent", [&] () { }); }); + + gen_service_node(xml, [&] () { + xml.node("parent", [&] () { + xml.attribute("label", Label("leitzentrale -> editor")); }); }); + + gen_service_node<::File_system::Session>(xml, [&] () { + xml.attribute("label", "fonts"); + xml.node("parent", [&] () { + xml.attribute("label", "leitzentrale -> fonts"); }); }); + + gen_service_node<::File_system::Session>(xml, [&] () { + + if (browsed_fs == "config") { + xml.node("parent", [&] () { + xml.attribute("label", "config"); }); + } + else if (browsed_fs == "report") { + xml.node("parent", [&] () { + xml.attribute("label", "report"); }); + } + else { + xml.node("child", [&] () { + xml.attribute("name", browsed_fs); }); + } + }); + }); + }); + } +}; + +#endif /* _MODEL__FILE_BROWSER_STATE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/partition.h b/repos/gems/src/app/sculpt_manager/model/partition.h index 5d4941dbc..9a0657e81 100644 --- a/repos/gems/src/app/sculpt_manager/model/partition.h +++ b/repos/gems/src/app/sculpt_manager/model/partition.h @@ -31,6 +31,8 @@ struct Sculpt::File_system { enum Type { UNKNOWN, EXT2, FAT32, GEMDOS } type; + bool inspected = false; + bool accessible() const { return type == EXT2 || type == FAT32 || type == GEMDOS; } bool expandable() const { return type == EXT2; } @@ -58,7 +60,6 @@ struct Sculpt::Partition : List_model::Element bool check_in_progress = false; bool format_in_progress = false; - bool file_system_inspected = false; bool gpt_expand_in_progress = false; bool fs_resize_in_progress = false; @@ -76,7 +77,7 @@ struct Sculpt::Partition : List_model::Element bool idle() const { return !check_in_progress && !format_in_progress - && !file_system_inspected + && !file_system.inspected && !relabel_in_progress(); } bool genode_default() const { return label == "GENODE*"; } diff --git a/repos/gems/src/app/sculpt_manager/model/ram_fs_state.h b/repos/gems/src/app/sculpt_manager/model/ram_fs_state.h index a3789cb00..85d7bd852 100644 --- a/repos/gems/src/app/sculpt_manager/model/ram_fs_state.h +++ b/repos/gems/src/app/sculpt_manager/model/ram_fs_state.h @@ -20,15 +20,17 @@ /* local includes */ #include #include +#include namespace Sculpt { struct Ram_fs_state; } -struct Sculpt::Ram_fs_state : Child_state +struct Sculpt::Ram_fs_state : Child_state, File_system { - bool inspected = false; - - Ram_fs_state(Start_name const &name) - : Child_state(name, Ram_quota{1024*1024}, Cap_quota{300}) { } + Ram_fs_state(Registry ®istry, Start_name const &name) + : + Child_state(registry, name, Ram_quota{1024*1024}, Cap_quota{300}), + File_system(File_system::UNKNOWN) + { } }; #endif /* _MODEL__RAM_FS_STATE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_config.h b/repos/gems/src/app/sculpt_manager/model/runtime_config.h index fa86ce574..c9eec3aae 100644 --- a/repos/gems/src/app/sculpt_manager/model/runtime_config.h +++ b/repos/gems/src/app/sculpt_manager/model/runtime_config.h @@ -69,9 +69,7 @@ class Sculpt::Runtime_config if (ignored_service) return; - bool const hardware = (service == "Block") - || (service == "USB") - || (service == "Platform") + bool const hardware = (service == "Platform") || (service == "IO_PORT") || (service == "IO_MEM") || (service == "Rtc") @@ -82,6 +80,18 @@ class Sculpt::Runtime_config return; } + bool const usb = (service == "Usb"); + if (usb) { + result = "usb"; + return; + } + + bool const storage = (service == "Block"); + if (storage) { + result = "storage"; + return; + } + if (service == "ROM") { /* @@ -347,6 +357,10 @@ class Sculpt::Runtime_config } _parent_services { }; + Service const _used_fs_service { "default_fs_rw", + Service::Type::FILE_SYSTEM, + Label(), "used file system" }; + public: Runtime_config(Allocator &alloc) : _alloc(alloc) { } @@ -375,10 +389,12 @@ class Sculpt::Runtime_config template void for_each_service(FN const &fn) const { + _parent_services.for_each(fn); + + fn(_used_fs_service); + _components.for_each([&] (Component const &component) { component.for_each_service(fn); }); - - _parent_services.for_each(fn); } }; diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_state.h b/repos/gems/src/app/sculpt_manager/model/runtime_state.h index 5c6e9119a..be182fc97 100644 --- a/repos/gems/src/app/sculpt_manager/model/runtime_state.h +++ b/repos/gems/src/app/sculpt_manager/model/runtime_state.h @@ -70,6 +70,9 @@ class Sculpt::Runtime_state : public Runtime_info List_model _children { }; + bool _usb_in_tcb = false; + bool _storage_in_tcb = false; + /** * Child present in initial deploy config but interactively removed */ @@ -264,6 +267,9 @@ class Sculpt::Runtime_state : public Runtime_info return result; } + bool usb_in_tcb() const { return _usb_in_tcb; } + bool storage_in_tcb() const { return _storage_in_tcb; } + static bool blacklisted_from_graph(Start_name const &name) { /* @@ -281,6 +287,8 @@ class Sculpt::Runtime_state : public Runtime_info child.info.tcb_updated = false; }); + _usb_in_tcb = _storage_in_tcb = false; + /* * Update the TCB flag of the selected child's transitive * dependencies. @@ -315,6 +323,15 @@ class Sculpt::Runtime_state : public Runtime_info child.info.tcb = true; }); }); } + + _children.for_each([&] (Child const &child) { + if (child.info.tcb) { + config.for_each_dependency(child.name, [&] (Start_name dep) { + if (dep == "usb") _usb_in_tcb = true; + if (dep == "storage") _storage_in_tcb = true; + }); + } + }); } void abandon(Start_name const &name) diff --git a/repos/gems/src/app/sculpt_manager/model/service.h b/repos/gems/src/app/sculpt_manager/model/service.h index 0267b6e2c..a57aa6b69 100644 --- a/repos/gems/src/app/sculpt_manager/model/service.h +++ b/repos/gems/src/app/sculpt_manager/model/service.h @@ -72,6 +72,12 @@ struct Sculpt::Service Service(Start_name const &server, Type type, Label const &label) : server(server), type(type), label(label), info(Subst("_", " ", server)) { } + /** + * Constructor for default_fs_rw + */ + Service(Start_name const &server, Type type, Label const &label, Info const &info) + : server(server), type(type), label(label), info(info) { } + /** * Constructor for parent service */ diff --git a/repos/gems/src/app/sculpt_manager/model/settings.h b/repos/gems/src/app/sculpt_manager/model/settings.h new file mode 100644 index 000000000..4f82e6419 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/model/settings.h @@ -0,0 +1,23 @@ +/* + * \brief Settings state + * \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 _MODEL__SETTINGS_H_ +#define _MODEL__SETTINGS_H_ + +#include "types.h" + +namespace Sculpt { enum class Font_size; } + +enum class Sculpt::Font_size { SMALL, MEDIUM, LARGE }; + +#endif /* _MODEL__SETTINGS_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/storage_device.h b/repos/gems/src/app/sculpt_manager/model/storage_device.h index 95762972f..721496bce 100644 --- a/repos/gems/src/app/sculpt_manager/model/storage_device.h +++ b/repos/gems/src/app/sculpt_manager/model/storage_device.h @@ -83,7 +83,7 @@ struct Sculpt::Storage_device * * Ignore reports that come in while the device is in use. Otherwise, * the reconstruction of 'whole_device_partition' would wrongly reset - * the partition state such as the 'file_system_inspected' flag. + * the partition state such as the 'file_system.inspected' flag. */ if (!whole_device_partition.constructed() || whole_device_partition->idle()) { whole_device_partition.construct(Partition::Args::whole_device(capacity)); @@ -130,7 +130,7 @@ struct Sculpt::Storage_device partitions.for_each([&] (Partition const &partition) { needed_for_access |= partition.check_in_progress; needed_for_access |= partition.format_in_progress; - needed_for_access |= partition.file_system_inspected; + needed_for_access |= partition.file_system.inspected; needed_for_access |= partition.fs_resize_in_progress; }); diff --git a/repos/gems/src/app/sculpt_manager/model/storage_devices.h b/repos/gems/src/app/sculpt_manager/model/storage_devices.h index e6051470a..ca69af954 100644 --- a/repos/gems/src/app/sculpt_manager/model/storage_devices.h +++ b/repos/gems/src/app/sculpt_manager/model/storage_devices.h @@ -29,6 +29,8 @@ struct Sculpt::Storage_devices bool _block_devices_report_valid = false; bool _usb_active_config_valid = false; + bool usb_present = false; + /** * Update 'block_devices' from 'block_devices' report */ @@ -51,6 +53,10 @@ struct Sculpt::Storage_devices if (node.has_type("raw")) _usb_active_config_valid = true; + + usb_present = false; + usb_storage_devices.for_each([&] (Storage_device const &) { + usb_present = true; }); } /** diff --git a/repos/gems/src/app/sculpt_manager/model/usb_storage_device.h b/repos/gems/src/app/sculpt_manager/model/usb_storage_device.h index 6e89452ab..faf5428cd 100644 --- a/repos/gems/src/app/sculpt_manager/model/usb_storage_device.h +++ b/repos/gems/src/app/sculpt_manager/model/usb_storage_device.h @@ -74,7 +74,7 @@ struct Sculpt::Usb_storage_device : List_model::Element, for_each_partition([&] (Partition const &partition) { drv_needed |= partition.check_in_progress || partition.format_in_progress - || partition.file_system_inspected + || partition.file_system.inspected || partition.relabel_in_progress() || partition.expand_in_progress(); }); @@ -123,6 +123,10 @@ void Sculpt::Usb_storage_device::gen_usb_block_drv_start_content(Xml_generator & gen_provides(xml); xml.node("route", [&] () { + gen_service_node(xml, [&] () { + xml.node("parent", [&] () { + xml.attribute("label", label); }); }); + gen_parent_rom_route(xml, "usb_block_drv"); gen_parent_rom_route(xml, "ld.lib.so"); gen_parent_route (xml); @@ -130,10 +134,6 @@ void Sculpt::Usb_storage_device::gen_usb_block_drv_start_content(Xml_generator & gen_parent_route (xml); gen_parent_route (xml); - gen_service_node(xml, [&] () { - xml.node("parent", [&] () { - xml.attribute("label", label); }); }); - gen_service_node(xml, [&] () { xml.node("parent", [&] () { }); }); }); diff --git a/repos/gems/src/app/sculpt_manager/network.cc b/repos/gems/src/app/sculpt_manager/network.cc index 1aaeb17b2..f3ba783dc 100644 --- a/repos/gems/src/app/sculpt_manager/network.cc +++ b/repos/gems/src/app/sculpt_manager/network.cc @@ -50,7 +50,7 @@ void Sculpt::Network::handle_key_press(Codepoint code) if (_wifi_connection.state == Wifi_connection::CONNECTING) wifi_connect(_wifi_connection.bssid); - _dialog_generator.generate_dialog(); + _menu_view.generate(); } @@ -136,7 +136,7 @@ void Sculpt::Network::_handle_wlan_accesspoints() Access_point_update_policy policy(_alloc); _access_points.update_from_xml(policy, _wlan_accesspoints_rom.xml()); - _dialog_generator.generate_dialog(); + _menu_view.generate(); } @@ -144,7 +144,7 @@ void Sculpt::Network::_handle_wlan_state() { _wlan_state_rom.update(); _wifi_connection = Wifi_connection::from_xml(_wlan_state_rom.xml()); - _dialog_generator.generate_dialog(); + _menu_view.generate(); } @@ -156,7 +156,7 @@ void Sculpt::Network::_handle_nic_router_state() _nic_state = Nic_state::from_xml(_nic_router_state_rom.xml()); if (_nic_state.ipv4 != old_nic_state.ipv4) - _dialog_generator.generate_dialog(); + _menu_view.generate(); /* if the nic state becomes ready, consider spawning the update subsystem */ if (old_nic_state.ready() != _nic_state.ready()) @@ -210,7 +210,7 @@ void Sculpt::Network::_handle_nic_router_config(Xml_node config) nic_target(target); _generate_nic_router_config(); _runtime_config_generator.generate_runtime_config(); - _dialog_generator.generate_dialog(); + _menu_view.generate(); } diff --git a/repos/gems/src/app/sculpt_manager/network.h b/repos/gems/src/app/sculpt_manager/network.h index 0608be3d0..a3b569f33 100644 --- a/repos/gems/src/app/sculpt_manager/network.h +++ b/repos/gems/src/app/sculpt_manager/network.h @@ -21,7 +21,7 @@ /* local includes */ #include #include -#include +#include #include #include #include @@ -35,7 +35,7 @@ struct Sculpt::Network : Network_dialog::Action Allocator &_alloc; - Dialog::Generator &_dialog_generator; + Registry &_child_states; Runtime_config_generator &_runtime_config_generator; @@ -97,10 +97,22 @@ struct Sculpt::Network : Network_dialog::Action Network_dialog::WLAN_CONFIG_MANAGED; Network_dialog dialog { - _env, _dialog_generator, _nic_target, _access_points, + _nic_target, _access_points, _wifi_connection, _nic_state, wpa_passphrase, _wlan_config_policy, _pci_info }; + Menu_view _menu_view { _env, _child_states, dialog, "network_view", + Ram_quota{4*1024*1024}, Cap_quota{150}, + "network_dialog", "network_view_hover" }; + + void min_dialog_width(unsigned value) { _menu_view.min_width = value; } + + bool dialog_hovered() const { return _menu_view.hovered(); } + + void update_view() { _menu_view.generate(); } + + void trigger_dialog_restart() { _menu_view.trigger_restart(); } + Managed_config _wlan_config { _env, "config", "wifi", *this, &Network::_handle_wlan_config }; @@ -108,7 +120,7 @@ struct Sculpt::Network : Network_dialog::Action { if (_wlan_config.try_generate_manually_managed()) { _wlan_config_policy = Network_dialog::WLAN_CONFIG_MANUAL; - _dialog_generator.generate_dialog(); + _menu_view.generate(); return; } @@ -142,7 +154,7 @@ struct Sculpt::Network : Network_dialog::Action _nic_target.managed_type = type; _generate_nic_router_config(); _runtime_config_generator.generate_runtime_config(); - _dialog_generator.generate_dialog(); + _menu_view.generate(); } } @@ -207,11 +219,11 @@ struct Sculpt::Network : Network_dialog::Action _runtime_config_generator.generate_runtime_config(); } - Network(Env &env, Allocator &alloc, Dialog::Generator &dialog_generator, + Network(Env &env, Allocator &alloc, Registry &child_states, Runtime_config_generator &runtime_config_generator, Runtime_info const &runtime_info, Pci_info const &pci_info) : - _env(env), _alloc(alloc), _dialog_generator(dialog_generator), + _env(env), _alloc(alloc), _child_states(child_states), _runtime_config_generator(runtime_config_generator), _runtime_info(runtime_info), _pci_info(pci_info) { diff --git a/repos/gems/src/app/sculpt_manager/runtime/file_browser.cc b/repos/gems/src/app/sculpt_manager/runtime/file_browser.cc index 7db7fcc13..d3d568225 100644 --- a/repos/gems/src/app/sculpt_manager/runtime/file_browser.cc +++ b/repos/gems/src/app/sculpt_manager/runtime/file_browser.cc @@ -20,7 +20,7 @@ namespace Sculpt { { devices.for_each([&] (Storage_device const &device) { device.for_each_partition([&] (Partition const &partition) { - if (partition.file_system_inspected) + if (partition.file_system.inspected) fn(Storage_target { device.label, partition.number }); }); }); } diff --git a/repos/gems/src/app/sculpt_manager/runtime/runtime_view.cc b/repos/gems/src/app/sculpt_manager/runtime/runtime_view.cc index 062d9cecc..0e5d1f2d0 100644 --- a/repos/gems/src/app/sculpt_manager/runtime/runtime_view.cc +++ b/repos/gems/src/app/sculpt_manager/runtime/runtime_view.cc @@ -52,11 +52,13 @@ void Sculpt::gen_runtime_view_start_content(Xml_generator &xml, gen_service_node(xml, [&] () { xml.attribute("label", "dialog"); - xml.node("parent", [&] () { }); }); + xml.node("parent", [&] () { + xml.attribute("label", "leitzentrale -> runtime_view -> dialog"); }); }); gen_service_node(xml, [&] () { xml.attribute("label", "hover"); - xml.node("parent", [&] () { }); }); + xml.node("parent", [&] () { + xml.attribute("label", "leitzentrale -> runtime_view -> hover"); }); }); gen_parent_rom_route(xml, "menu_view"); gen_parent_rom_route(xml, "ld.lib.so"); diff --git a/repos/gems/src/app/sculpt_manager/storage.cc b/repos/gems/src/app/sculpt_manager/storage.cc index e24688818..b5f42ff58 100644 --- a/repos/gems/src/app/sculpt_manager/storage.cc +++ b/repos/gems/src/app/sculpt_manager/storage.cc @@ -140,7 +140,7 @@ void Sculpt::Storage::gen_runtime_start_nodes(Xml_generator &xml) const gen_resize2fs_start_content(xml, target); }); } if (partition.file_system.type != File_system::UNKNOWN) { - if (partition.file_system_inspected || target == _sculpt_partition) + if (partition.file_system.inspected || target == _sculpt_partition) xml.node("start", [&] () { gen_fs_start_content(xml, target, partition.file_system.type); }); diff --git a/repos/gems/src/app/sculpt_manager/storage.h b/repos/gems/src/app/sculpt_manager/storage.h index a65a35044..6c1bad0eb 100644 --- a/repos/gems/src/app/sculpt_manager/storage.h +++ b/repos/gems/src/app/sculpt_manager/storage.h @@ -21,12 +21,13 @@ /* local includes */ #include #include +#include #include namespace Sculpt { struct Storage; } -struct Sculpt::Storage : Storage_dialog::Action +struct Sculpt::Storage : Storage_dialog::Action, Ram_fs_dialog::Action { Env &_env; @@ -49,7 +50,7 @@ struct Sculpt::Storage : Storage_dialog::Action Storage_devices _storage_devices { }; - Ram_fs_state _ram_fs_state { "ram_fs" }; + Ram_fs_state _ram_fs_state; Storage_target _sculpt_partition { }; @@ -57,8 +58,7 @@ struct Sculpt::Storage : Storage_dialog::Action File_browser_version _file_browser_version { 0 }; - Storage_dialog dialog { - _env, _dialog_generator, _storage_devices, _ram_fs_state, _sculpt_partition }; + Storage_dialog dialog { _storage_devices, _sculpt_partition }; void handle_storage_devices_update(); @@ -73,7 +73,7 @@ struct Sculpt::Storage : Storage_dialog::Action bool result = _ram_fs_state.inspected; _storage_devices.for_each([&] (Storage_device const &device) { device.for_each_partition([&] (Partition const &partition) { - result |= partition.file_system_inspected; }); }); + result |= partition.file_system.inspected; }); }); return result; } @@ -159,7 +159,7 @@ struct Sculpt::Storage : Storage_dialog::Action } _apply_partition(target, [&] (Partition &partition) { - partition.file_system_inspected = !partition.file_system_inspected; + partition.file_system.inspected = !partition.file_system.inspected; _file_browser_version.value++; }); @@ -189,14 +189,17 @@ struct Sculpt::Storage : Storage_dialog::Action } - Storage(Env &env, Allocator &alloc, Dialog::Generator &dialog_generator, + Storage(Env &env, Allocator &alloc, + Registry &child_states, + Dialog::Generator &dialog_generator, Runtime_config_generator &runtime_config_generator, - Target_user &target_user) + Target_user &target_user) : _env(env), _alloc(alloc), _dialog_generator(dialog_generator), _runtime_config_generator(runtime_config_generator), - _target_user(target_user) + _target_user(target_user), + _ram_fs_state(child_states, "ram_fs") { _block_devices_rom .sigh(_storage_device_update_handler); _usb_active_config_rom.sigh(_storage_device_update_handler); diff --git a/repos/gems/src/app/sculpt_manager/view/dialog.h b/repos/gems/src/app/sculpt_manager/view/dialog.h index 674e1a646..57ce424c7 100644 --- a/repos/gems/src/app/sculpt_manager/view/dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/dialog.h @@ -16,9 +16,11 @@ /* Genode includes */ #include +#include /* local includes */ #include "types.h" +#include namespace Sculpt { struct Dialog; } @@ -33,7 +35,68 @@ struct Sculpt::Dialog : Interface */ struct Generator : Interface { virtual void generate_dialog() = 0; }; - virtual void hover(Xml_node hover) = 0; + 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 + 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 + Hover_result _match_sub_dialog(Xml_node hover, char const *sub_node, + TAIL &&... tail) + { + Hover_result result = Hover_result::UNMODIFIED; + + hover.with_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 + 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("")); + + return result; + } + }; #endif /* _VIEW__DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/file_browser_dialog.h b/repos/gems/src/app/sculpt_manager/view/file_browser_dialog.h new file mode 100644 index 000000000..7b0c8e056 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/file_browser_dialog.h @@ -0,0 +1,381 @@ +/* + * \brief File-browser dialog + * \author Norman Feske + * \date 2020-01-31 + */ + +/* + * 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__FILE_BROWSER_DIALOG_H_ +#define _VIEW__FILE_BROWSER_DIALOG_H_ + +#include +#include +#include + +namespace Sculpt { struct File_browser_dialog; } + + +struct Sculpt::File_browser_dialog : Noncopyable, 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; + + 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>; + + template + void _for_each_path_elem(FN const &fn) const + { + char const *curr = _state.path.string(); + + fn(Path("/")); + + for (;;) { + + if (*curr != '/') + return; + + curr++; /* skip '/' */ + + /* search next '/' */ + size_t len = 0; + for (; curr[len] != 0 && curr[len] != '/'; len++); + + fn(Path(Cstring(curr, len))); + + curr += len; + } + } + + Path _path_elem_at_index(unsigned index) const + { + Path result { }; + unsigned i = 0; + + _for_each_path_elem([&] (Path const &elem) { + if (i++ == index) + result = elem; }); + + return result; + } + + void _gen_path_elements(Xml_generator &xml) const + { + gen_named_node(xml, "hbox", "back", [&] () { + unsigned i = 0; + _for_each_path_elem([&] (Path const elem) { + + 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); + }); + }); + i++; + }); + }); + } + + void _gen_back_entry(Xml_generator &xml) const + { + gen_named_node(xml, "hbox", "back", [&] () { + + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", "yes"); + + xml.node("hbox", [&] () { + if (!_state.modified) { + gen_named_node(xml, "button", "button", [&] () { + xml.attribute("style", "back"); + _entry.gen_hovered_attr(xml, "back"); + xml.node("hbox", [&] () { }); + }); + } + + _gen_path_elements(xml); + }); + }); + + gen_named_node(xml, "hbox", "right", [&] () { }); + }); + } + + void _gen_entry(Xml_generator &xml, Xml_node node, Index const &index, + char const *style) const + { + Name const name = node.attribute_value("name", Name()); + + bool const entry_edited = (name == _state.edited_file); + + /* while editing one file, hide all others */ + if (!entry_edited && _state.modified) + return; + + gen_named_node(xml, "hbox", index, [&] () { + + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", "yes"); + + xml.node("hbox", [&] () { + gen_named_node(xml, "button", "button", [&] () { + + bool const selected = (name == _state.edited_file); + if (selected) + xml.attribute("selected", "yes"); + + xml.attribute("style", style); + + 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"); + + /* show no operation buttons for directories */ + if (node.type() == "dir") + return; + + gen_named_node(xml, "hbox", "right", [&] () { + + bool const entry_hovered = _entry.hovered(index); + bool const interesting = entry_hovered || entry_edited; + bool const writeable = node.attribute_value("writeable", false); + + if (writeable) { + + 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 (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"); + }); + }); + } + }); + }); + }); + } + + void _gen_file_system(Xml_generator &xml, Service const &service) const + { + bool const parent = !service.server.valid(); + Label const name = parent ? Start_name(service.label) : service.server; + + Start_name const pretty_name { Pretty(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"); }); + + gen_named_node(xml, "button", name, [&] () { + if (!_state.modified) + _fs_button.gen_hovered_attr(xml, name); + + if (selected) + xml.attribute("selected", true); + + xml.node("label", [&] () { + xml.attribute("text", pretty_name); + xml.attribute("style", "title"); + }); + }); + + if (selected) { + unsigned cnt = 0; + + _state.with_query_result([&] (Xml_node node) { + node.with_sub_node("dir", [&] (Xml_node listing) { + + if (_state.path != "/") + _gen_back_entry(xml); + + listing.for_each_sub_node("dir", [&] (Xml_node dir) { + _gen_entry(xml, dir, Index(cnt++), "enter"); }); + + listing.for_each_sub_node("file", [&] (Xml_node file) { + _gen_entry(xml, file, Index(cnt++), "radio"); }); + }); + }); + } + }); + }); + } + + void generate(Xml_generator &xml) const override + { + xml.node("vbox", [&] () { + _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; + } + + File_browser_dialog(Runtime_config const &runtime_config, + File_browser_state const &state) + : + _runtime_config(runtime_config), _state(state) + { } +}; + +#endif /* _VIEW__FILE_BROWSER_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/fs_dialog.h b/repos/gems/src/app/sculpt_manager/view/fs_dialog.h new file mode 100644 index 000000000..e13663ccd --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/fs_dialog.h @@ -0,0 +1,89 @@ +/* + * \brief Common part of file-system management dialogs + * \author Norman Feske + * \date 2020-01-28 + */ + +/* + * 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__FS_DIALOG_H_ +#define _VIEW__FS_DIALOG_H_ + +#include +#include + +namespace Sculpt { struct Fs_dialog; } + + +struct Sculpt::Fs_dialog : Noncopyable, Dialog +{ + Storage_target const _target; + Storage_target const &_used_target; + + Hoverable_item _inspect_item { }; + Hoverable_item _use_item { }; + + Hover_result hover(Xml_node hover) override + { + return any_hover_changed( + _inspect_item.match(hover, "button", "name"), + _use_item .match(hover, "button", "name")); + } + + void reset() override { } + + struct Action : Interface + { + virtual void toggle_file_browser(Storage_target const &) = 0; + + virtual void use(Storage_target const &) = 0; + }; + + void generate(Xml_generator &) const override { } + + void generate(Xml_generator &xml, File_system const &file_system) const + { + xml.node("button", [&] () { + _inspect_item.gen_button_attr(xml, "browse"); + + if (file_system.inspected) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", "Inspect"); }); + }); + + if (!_used_target.valid() || _used_target == _target) { + xml.node("button", [&] () { + _use_item.gen_button_attr(xml, "use"); + if (_used_target == _target) + xml.attribute("selected", "yes"); + xml.node("label", [&] () { xml.attribute("text", "Use"); }); + }); + } + } + + Click_result click(Action &action) + { + if (_inspect_item.hovered("browse")) + action.toggle_file_browser(_target); + + else if (_use_item.hovered("use")) + action.use((_used_target == _target) ? Storage_target{ } : _target); + + else return Click_result::IGNORED; + + return Click_result::CONSUMED; + } + + Fs_dialog(Storage_target target, Storage_target const &used_target) + : + _target(target), _used_target(used_target) + { } +}; + +#endif /* _VIEW__FS_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/hoverable_item.h b/repos/gems/src/app/sculpt_manager/view/hoverable_item.h index b34c9472b..c5f66dde1 100644 --- a/repos/gems/src/app/sculpt_manager/view/hoverable_item.h +++ b/repos/gems/src/app/sculpt_manager/view/hoverable_item.h @@ -26,17 +26,19 @@ struct Sculpt::Hoverable_item Id _hovered { }; + enum class Hover_result { CHANGED, UNMODIFIED }; + /** * Set ID to matching hover sub node name * * \return true if hovering changed */ template - bool match(Xml_node hover, ARGS &&... args) + Hover_result match(Xml_node hover, ARGS &&... args) { Id const orig = _hovered; _hovered = query_attribute(hover, args...); - return _hovered != orig; + return (_hovered != orig) ? Hover_result::CHANGED : Hover_result::UNMODIFIED; } /** diff --git a/repos/gems/src/app/sculpt_manager/view/network_dialog.cc b/repos/gems/src/app/sculpt_manager/view/network_dialog.cc index cf368a208..49bd64b57 100644 --- a/repos/gems/src/app/sculpt_manager/view/network_dialog.cc +++ b/repos/gems/src/app/sculpt_manager/view/network_dialog.cc @@ -17,8 +17,10 @@ /* local includes */ #include "network_dialog.h" -void Sculpt::Network_dialog::_gen_access_point(Xml_generator &xml, - Access_point const &ap) const +using namespace Sculpt; + +void Network_dialog::_gen_access_point(Xml_generator &xml, + Access_point const &ap) const { gen_named_node(xml, "hbox", ap.bssid, [&] () { @@ -59,7 +61,7 @@ void Sculpt::Network_dialog::_gen_access_point(Xml_generator &xml, } -bool Sculpt::Network_dialog::_selected_ap_visible() const +bool Network_dialog::_selected_ap_visible() const { unsigned cnt = 0; return _for_each_ap([&] (Access_point const &ap) { @@ -67,14 +69,14 @@ bool Sculpt::Network_dialog::_selected_ap_visible() const } -bool Sculpt::Network_dialog::_selected_ap_unprotected() const +bool Network_dialog::_selected_ap_unprotected() const { return _for_each_ap([&] (Access_point const &ap) { return _ap_item.selected(ap.bssid) && ap.unprotected(); }); } -bool Sculpt::Network_dialog::need_keyboard_focus_for_passphrase() const +bool Network_dialog::need_keyboard_focus_for_passphrase() const { if ( _wifi_connection.state == Wifi_connection::CONNECTED || _wifi_connection.state == Wifi_connection::CONNECTING) @@ -88,8 +90,8 @@ bool Sculpt::Network_dialog::need_keyboard_focus_for_passphrase() const } -void Sculpt::Network_dialog::_gen_access_point_list(Xml_generator &xml, - bool auth_failure) const +void Network_dialog::_gen_access_point_list(Xml_generator &xml, + bool auth_failure) const { if (_wlan_config_policy == WLAN_CONFIG_MANUAL) return; @@ -133,7 +135,11 @@ void Sculpt::Network_dialog::_gen_access_point_list(Xml_generator &xml, xml.attribute("west", "yes"); xml.node("label", [&] () { xml.attribute("font", "title/regular"); - xml.attribute("text", String<3*64>(" ", _wpa_passphrase)); + String<3*64> const passphrase(" ", _wpa_passphrase); + xml.attribute("text", passphrase); + xml.node("cursor", [&] () { + xml.attribute("at", passphrase.length() - 1); + }); }); }); }); @@ -165,7 +171,7 @@ void Sculpt::Network_dialog::_gen_access_point_list(Xml_generator &xml, } -void Sculpt::Network_dialog::_gen_connected_ap(Xml_generator &xml, bool connected) const +void Network_dialog::_gen_connected_ap(Xml_generator &xml, bool connected) const { bool done = false; @@ -196,14 +202,10 @@ void Sculpt::Network_dialog::_gen_connected_ap(Xml_generator &xml, bool connecte } -void Sculpt::Network_dialog::generate(Xml_generator &xml) const +void Network_dialog::generate(Xml_generator &xml) const { gen_named_node(xml, "frame", "network", [&] () { xml.node("vbox", [&] () { - gen_named_node(xml, "label", "title", [&] () { - xml.attribute("text", "Network"); - xml.attribute("font", "title/regular"); - }); gen_named_node(xml, "hbox", "type", [&] () { @@ -270,21 +272,20 @@ void Sculpt::Network_dialog::generate(Xml_generator &xml) const } -void Sculpt::Network_dialog::hover(Xml_node hover) +Dialog::Hover_result Network_dialog::hover(Xml_node hover) { - bool const changed = - _nic_item .match(hover, "vbox", "hbox", "button", "name") | - _ap_item .match(hover, "vbox", "frame", "vbox", "hbox", "name") | - _connect_item.match(hover, "vbox", "frame", "vbox", "button", "name"); + Dialog::Hover_result const hover_result = 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"); - if (changed) - _dialog_generator.generate_dialog(); + return hover_result; } -void Sculpt::Network_dialog::click(Action &action) +void Network_dialog::click(Action &action) { if (_nic_item.hovered("off")) action.nic_target(Nic_target::OFF); if (_nic_item.hovered("local")) action.nic_target(Nic_target::LOCAL); @@ -304,6 +305,5 @@ void Sculpt::Network_dialog::click(Action &action) if (_connect_item.hovered("connect")) action.wifi_connect(selected_ap()); - - _dialog_generator.generate_dialog(); } + diff --git a/repos/gems/src/app/sculpt_manager/view/network_dialog.h b/repos/gems/src/app/sculpt_manager/view/network_dialog.h index 44e17c897..07b15f39e 100644 --- a/repos/gems/src/app/sculpt_manager/view/network_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/network_dialog.h @@ -14,24 +14,21 @@ #ifndef _VIEW__NETWORK_DIALOG_H_ #define _VIEW__NETWORK_DIALOG_H_ +/* local includes */ #include #include #include #include #include #include -#include #include +#include namespace Sculpt { struct Network_dialog; } struct Sculpt::Network_dialog : Dialog { - Env &_env; - - Dialog::Generator &_dialog_generator; - enum Wlan_config_policy { WLAN_CONFIG_MANAGED, WLAN_CONFIG_MANUAL }; Nic_target const &_nic_target; @@ -76,15 +73,14 @@ struct Sculpt::Network_dialog : Dialog void _gen_connected_ap(Xml_generator &, bool) const; void _gen_access_point_list(Xml_generator &, bool) const; - void generate(Xml_generator &) const; + Hover_result hover(Xml_node) override; + + void generate(Xml_generator &) const override; + + void reset() override { } bool need_keyboard_focus_for_passphrase() const; - /** - * Dialog interface - */ - void hover(Xml_node hover) override; - struct Action : Interface { virtual void nic_target(Nic_target::Type) = 0; @@ -96,9 +92,7 @@ struct Sculpt::Network_dialog : Dialog void click(Action &action); - Network_dialog(Env &env, - Dialog::Generator &dialog_generator, - Nic_target const &nic_target, + Network_dialog(Nic_target const &nic_target, Access_points const &access_points, Wifi_connection const &wifi_connection, Nic_state const &nic_state, @@ -106,7 +100,6 @@ struct Sculpt::Network_dialog : Dialog Wlan_config_policy const &wlan_config_policy, Pci_info const &pci_info) : - _env(env), _dialog_generator(dialog_generator), _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), diff --git a/repos/gems/src/app/sculpt_manager/view/panel_dialog.cc b/repos/gems/src/app/sculpt_manager/view/panel_dialog.cc new file mode 100644 index 000000000..c3988b21c --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/panel_dialog.cc @@ -0,0 +1,87 @@ +/* + * \brief Panel dialog + * \author Norman Feske + * \date 2020-01-26 + */ + +/* + * 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. + */ + +#include + +using namespace Sculpt; + + +void Panel_dialog::generate(Xml_generator &xml) const +{ + xml.node("frame", [&] () { + xml.attribute("style", "unimportant"); + + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", true); + xml.node("hbox", [&] () { + xml.node("button", [&] () { + _item.gen_button_attr(xml, "settings"); + if (_state.settings_visible()) + xml.attribute("selected", true); + xml.node("label", [&] () { + xml.attribute("text", "Settings"); + }); + }); + }); + }); + + gen_named_node(xml, "float", "center", [&] () { + xml.node("hbox", [&] () { + + auto gen_tab = [&] (auto name, auto text, Tab tab) { + + gen_named_node(xml, "button", name, [&] () { + + if (_state.selected_tab() == tab) + xml.attribute("selected", true); + else + _item.gen_hovered_attr(xml, name); + + xml.node("label", [&] () { + xml.attribute("text", text); + }); + }); + }; + + gen_tab("files", "Files", Tab::FILES); + gen_tab("components", "Components", Tab::COMPONENTS); + + if (_state.inspect_tab_visible()) + gen_tab("inspect", "Inspect", Tab::INSPECT); + }); + }); + + gen_named_node(xml, "float", "right", [&] () { + xml.attribute("east", true); + xml.node("hbox", [&] () { + xml.node("button", [&] () { + _item.gen_button_attr(xml, "network"); + if (_state.network_visible()) + xml.attribute("selected", true); + xml.node("label", [&] () { + xml.attribute("text", "Network"); + }); + }); + xml.node("button", [&] () { + _item.gen_button_attr(xml, "log"); + if (_state.log_visible()) + xml.attribute("selected", true); + xml.node("label", [&] () { + xml.attribute("text", "Log"); + }); + }); + }); + }); + }); +} + diff --git a/repos/gems/src/app/sculpt_manager/view/panel_dialog.h b/repos/gems/src/app/sculpt_manager/view/panel_dialog.h new file mode 100644 index 000000000..6369a3019 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/panel_dialog.h @@ -0,0 +1,81 @@ +/* + * \brief Panel dialog + * \author Norman Feske + * \date 2020-01-26 + */ + +/* + * 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__PANEL_DIALOG_H_ +#define _VIEW__PANEL_DIALOG_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include +#include +#include + +namespace Sculpt { struct Panel_dialog; } + + +struct Sculpt::Panel_dialog : Dialog +{ + enum class Tab { FILES, COMPONENTS, INSPECT }; + + struct State : Interface, Noncopyable + { + virtual Tab selected_tab() const = 0; + virtual bool log_visible() const = 0; + virtual bool settings_visible() const = 0; + virtual bool network_visible() const = 0; + virtual bool inspect_tab_visible() const = 0; + }; + + State const &_state; + + struct Action : Interface + { + virtual void select_tab(Tab) = 0; + virtual void toggle_log_visibility() = 0; + virtual void toggle_settings_visibility() = 0; + virtual void toggle_network_visibility() = 0; + }; + + Hoverable_item _item { }; + Activatable_item _action_item { }; + + Hover_result hover(Xml_node hover) override + { + return any_hover_changed( + _item.match(hover, "frame", "float", "hbox", "button", "name")); + } + + void generate(Xml_generator &) const override; + + void click(Action &action) + { + if (_item.hovered("components")) action.select_tab(Tab::COMPONENTS); + if (_item.hovered("files")) action.select_tab(Tab::FILES); + if (_item.hovered("inspect")) action.select_tab(Tab::INSPECT); + if (_item.hovered("log")) action.toggle_log_visibility(); + if (_item.hovered("settings")) action.toggle_settings_visibility(); + if (_item.hovered("network")) action.toggle_network_visibility(); + } + + void reset() override + { + _item._hovered = Hoverable_item::Id(); + _action_item.reset(); + } + + Panel_dialog(State const &state) : _state(state) { } +}; + +#endif /* _VIEW__PANEL_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/partition_dialog.cc b/repos/gems/src/app/sculpt_manager/view/partition_dialog.cc new file mode 100644 index 000000000..c5491adf4 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/partition_dialog.cc @@ -0,0 +1,238 @@ +/* + * \brief Partition management dialog + * \author Norman Feske + * \date 2020-01-29 + */ + +/* + * 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. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include "partition_dialog.h" + +using namespace Sculpt; + + +void Partition_dialog::gen_operations(Xml_generator &xml, + Storage_device const &device, + Partition const &partition) const +{ + String<16> const version(device.label, ".", partition.number); + + bool const whole_device = !partition.number.valid(); + + bool const device_in_use = (_used_target.device == device.label); + + bool const target_in_use = (_used_target == _partition) + || (whole_device && device_in_use) + || partition.file_system.inspected; + + bool const relabel_in_progress = device.relabel_in_progress(); + + bool const expand_in_progress = device.expand_in_progress(); + + bool const format_selected = _operation_item.selected("format"); + + bool const expand_selected = _operation_item.selected("expand"); + + if (partition.file_system.accessible() + && !format_selected && !expand_selected && !expand_in_progress) { + + if (!partition.check_in_progress + && !partition.format_in_progress + && !relabel_in_progress) { + + _fs_dialog.generate(xml, partition.file_system); + } + + if ((device.all_partitions_idle() || partition.relabel_in_progress()) + && partition.genode() && !device_in_use) { + + xml.node("button", [&] () { + + /* support hovering only if no relabeling is in progress */ + if (partition.relabel_in_progress()) + xml.attribute("name", "relabel"); + else + _relabel_item.gen_button_attr(xml, "relabel"); + + xml.attribute("version", version); + + if (partition.genode_default() || partition.relabel_in_progress()) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { + xml.attribute("text", "Default"); }); + }); + if (partition.relabel_in_progress()) + xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); + } + + if (!target_in_use && !partition.format_in_progress && partition.checkable() + && !relabel_in_progress) { + + xml.node("button", [&] () { + _operation_item.gen_button_attr(xml, "check"); + xml.attribute("version", version); + + if (partition.check_in_progress) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", "Check"); }); + }); + if (partition.check_in_progress) + xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); + } + } + + bool const whole_device_with_partition_in_use = + whole_device && !device.all_partitions_idle(); + + bool const format_button_visible = !target_in_use + && !whole_device_with_partition_in_use + && !partition.check_in_progress + && !expand_in_progress + && !relabel_in_progress + && !_operation_item.selected("expand"); + + bool const expand_button_visible = !target_in_use + && !whole_device + && !partition.check_in_progress + && !partition.format_in_progress + && !relabel_in_progress + && partition.expandable() + && !_operation_item.selected("format"); + + bool const progress_msg_visible = + (_operation_item.selected("format") && partition.format_in_progress) + || (_operation_item.selected("expand") && partition.expand_in_progress()); + + bool const confirm_visible = + (_operation_item.selected("format") && !partition.format_in_progress) + || (_operation_item.selected("expand") && !partition.expand_in_progress()); + + if (format_button_visible) { + xml.node("button", [&] () { + _operation_item.gen_button_attr(xml, "format"); + xml.attribute("version", version); + + if (partition.format_in_progress) + xml.attribute("selected", "yes"); + + if (whole_device) { + xml.node("label", [&] () { xml.attribute("text", "Format device ..."); }); + } else { + xml.node("label", [&] () { xml.attribute("text", "Format partition ..."); }); + } + }); + } + + if (expand_button_visible) { + xml.node("button", [&] () { + _operation_item.gen_button_attr(xml, "expand"); + xml.attribute("version", version); + + if (partition.expand_in_progress()) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", "Expand ..."); }); + }); + } + + if (progress_msg_visible) + xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); + + if (confirm_visible) { + xml.node("button", [&] () { + _confirm_item.gen_button_attr(xml, "confirm"); + xml.attribute("version", version); + xml.node("label", [&] () { xml.attribute("text", "Confirm"); }); + }); + } +} + + +Dialog::Hover_result Partition_dialog::hover(Xml_node hover) +{ + Hover_result const hover_result = any_hover_changed( + _fs_dialog.hover(hover), + _relabel_item .match(hover, "button", "name"), + _operation_item.match(hover, "button", "name"), + _confirm_item .match(hover, "button", "name")); + + return hover_result; +} + + +Dialog::Click_result Partition_dialog::click(Action &action) +{ + if (_fs_dialog.click(action) == Click_result::CONSUMED) + return Click_result::CONSUMED; + + if (_operation_item.hovered("format")) { + if (_operation_item.selected("format")) + action.cancel_format(_partition); + else + _operation_item.toggle_selection_on_click(); + return Click_result::CONSUMED; + } + + if (_operation_item.hovered("expand")) { + if (_operation_item.selected("expand")) + action.cancel_expand(_partition); + else + _operation_item.toggle_selection_on_click(); + return Click_result::CONSUMED; + } + + if (_operation_item.hovered("check")) { + action.check(_partition); + return Click_result::CONSUMED; + } + + if (_relabel_item.hovered("relabel")) { + action.toggle_default_storage_target(_partition); + return Click_result::CONSUMED; + } + + if (_confirm_item.hovered("confirm")) { + _confirm_item.propose_activation_on_click(); + return Click_result::CONSUMED; + } + + return Click_result::IGNORED; +} + + +Dialog::Clack_result Partition_dialog::clack(Action &action) +{ + if (_confirm_item.hovered("confirm")) { + + _confirm_item.confirm_activation_on_clack(); + + if (_confirm_item.activated("confirm")) { + + if (_operation_item.selected("format")) { + action.format(_partition); + return Clack_result::CONSUMED; + } + + if (_operation_item.selected("expand")) { + action.expand(_partition); + return Clack_result::CONSUMED; + } + } + } else { + _confirm_item.reset(); + } + + return Clack_result::IGNORED; +} + diff --git a/repos/gems/src/app/sculpt_manager/view/partition_dialog.h b/repos/gems/src/app/sculpt_manager/view/partition_dialog.h new file mode 100644 index 000000000..86127b55e --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/partition_dialog.h @@ -0,0 +1,77 @@ +/* + * \brief Partition management dialog + * \author Norman Feske + * \date 2020-01-29 + */ + +/* + * 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__PARTITION_DIALOG_H_ +#define _VIEW__PARTITION_DIALOG_H_ + +#include +#include +#include +#include +#include +#include + +namespace Sculpt { struct Partition_dialog; } + + +struct Sculpt::Partition_dialog : Dialog +{ + Storage_target const _partition; + Storage_devices const &_storage_devices; + Storage_target const &_used_target; + + Hoverable_item _relabel_item { }; + Selectable_item _operation_item { }; + Activatable_item _confirm_item { }; + + Fs_dialog _fs_dialog { _partition, _used_target }; + + void generate(Xml_generator &) const override { } + + void gen_operations(Xml_generator &, Storage_device const &, Partition const &) const; + + Hover_result hover(Xml_node hover) override; + + struct Action : Fs_dialog::Action + { + virtual void format(Storage_target const &) = 0; + + virtual void cancel_format(Storage_target const &) = 0; + + virtual void expand(Storage_target const &) = 0; + + virtual void cancel_expand(Storage_target const &) = 0; + + virtual void check(Storage_target const &) = 0; + + virtual void toggle_default_storage_target(Storage_target const &) = 0; + }; + + void reset() override { } + + void reset_operation() { _operation_item.reset(); } + + Click_result click(Action &action); + Clack_result clack(Action &action); + + Partition_dialog(Storage_target const partition, + Storage_devices const &storage_devices, + Storage_target const &used) + : + _partition(partition), + _storage_devices(storage_devices), + _used_target(used) + { } +}; + +#endif /* _VIEW__PARTITION_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc index 4cbe59501..1b7b64bce 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc @@ -460,6 +460,4 @@ void Popup_dialog::click(Action &action) } } } - - generate(); } diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h index 8f3bf6200..be14bcb84 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h @@ -33,12 +33,10 @@ namespace Sculpt { struct Popup_dialog; } -struct Sculpt::Popup_dialog +struct Sculpt::Popup_dialog : Dialog { Env &_env; - Allocator &_alloc; - Sculpt_version const _sculpt_version { _env }; Launchers const &_launchers; @@ -71,6 +69,13 @@ struct Sculpt::Popup_dialog } }; + struct Refresh : Interface, Noncopyable + { + virtual void refresh_popup_dialog() = 0; + }; + + Refresh &_refresh; + struct Action : Interface { virtual void launch_global(Path const &launcher) = 0; @@ -103,20 +108,11 @@ struct Sculpt::Popup_dialog Construction_info const &_construction_info; - Expanding_reporter _dialog_reporter { _env, "dialog", "popup_dialog" }; - - Attached_rom_dataspace _hover_rom { _env, "popup_view_hover" }; - - Signal_handler _hover_handler { - _env.ep(), *this, &Popup_dialog::_handle_hover }; - Hoverable_item _item { }; Activatable_item _action_item { }; Activatable_item _install_item { }; Hoverable_item _route_item { }; - bool _hovered = false; - enum State { TOP_LEVEL, DEPOT_REQUESTED, DEPOT_SHOWN, DEPOT_SELECTION, INDEX_REQUESTED, INDEX_SHOWN, PKG_REQUESTED, PKG_SHOWN, ROUTE_SELECTED }; @@ -190,26 +186,17 @@ struct Sculpt::Popup_dialog Menu _menu { }; - void _handle_hover() + Hover_result hover(Xml_node hover) override { - _hover_rom.update(); + Dialog::Hover_result const hover_result = 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")); - Xml_node const hover = _hover_rom.xml(); - - _hovered = hover.has_sub_node("dialog"); - - bool const changed = - _item .match(hover, "dialog", "frame", "vbox", "hbox", "name") | - _action_item .match(hover, "dialog", "frame", "vbox", "button", "name") | - _install_item.match(hover, "dialog", "frame", "vbox", "float", "vbox", "float", "button", "name") | - _route_item .match(hover, "dialog", "frame", "vbox", "frame", "vbox", "hbox", "name"); - - if (changed) - generate(); + return hover_result; } - bool hovered() const { return _hovered; }; - Attached_rom_dataspace _scan_rom { _env, "report -> runtime/depot_query/scan" }; Signal_handler _scan_handler { @@ -223,7 +210,7 @@ struct Sculpt::Popup_dialog _state = DEPOT_SHOWN; if (_state != TOP_LEVEL) - generate(); + _refresh.refresh_popup_dialog(); } Attached_rom_dataspace _index_rom { _env, "report -> runtime/depot_query/index" }; @@ -242,7 +229,7 @@ struct Sculpt::Popup_dialog if (_state == INDEX_REQUESTED) _state = INDEX_SHOWN; - generate(); + _refresh.refresh_popup_dialog(); } bool _index_avail(User const &user) const @@ -373,12 +360,11 @@ struct Sculpt::Popup_dialog void _gen_pkg_elements (Xml_generator &, Component const &) const; void _gen_menu_elements(Xml_generator &) const; - void generate() + void generate(Xml_generator &xml) const override { - _dialog_reporter.generate([&] (Xml_generator &xml) { - xml.node("frame", [&] () { - xml.node("vbox", [&] () { - _gen_menu_elements(xml); }); }); }); + xml.node("frame", [&] () { + xml.node("vbox", [&] () { + _gen_menu_elements(xml); }); }); } void click(Action &action); @@ -397,25 +383,24 @@ struct Sculpt::Popup_dialog _construction_info.with_construction([&] (Component const &component) { action.trigger_download(component.path); _install_item.reset(); - generate(); + _refresh.refresh_popup_dialog(); }); } } - void reset() + void reset() override { _item._hovered = Hoverable_item::Id(); _route_item._hovered = Hoverable_item::Id(); _action_item.reset(); _install_item.reset(); - _hovered = false; _state = TOP_LEVEL; _selected_user = User(); _selected_route.destruct(); _menu._level = 0; } - Popup_dialog(Env &env, Allocator &alloc, + Popup_dialog(Env &env, Refresh &refresh, Launchers const &launchers, Nic_state const &nic_state, Nic_target const &nic_target, @@ -425,17 +410,14 @@ struct Sculpt::Popup_dialog Depot_query &depot_query, Construction_info const &construction_info) : - _env(env), _alloc(alloc), _launchers(launchers), + _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_query(depot_query), - _construction_info(construction_info) + _refresh(refresh), _construction_info(construction_info) { - _hover_rom.sigh(_hover_handler); _scan_rom.sigh(_scan_handler); _index_rom.sigh(_index_handler); - - generate(); } void gen_depot_query(Xml_generator &xml) const @@ -473,7 +455,7 @@ struct Sculpt::Popup_dialog if (construction.blueprint_known && !_pkg_missing && !_pkg_rom_missing) _state = PKG_SHOWN; - generate(); + _refresh.refresh_popup_dialog(); } bool interested_in_download() const diff --git a/repos/gems/src/app/sculpt_manager/view/ram_fs_dialog.h b/repos/gems/src/app/sculpt_manager/view/ram_fs_dialog.h new file mode 100644 index 000000000..dfa39ac91 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/ram_fs_dialog.h @@ -0,0 +1,119 @@ +/* + * \brief RAM file-system management dialog + * \author Norman Feske + * \date 2020-01-28 + */ + +/* + * 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__RAM_FS_DIALOG_H_ +#define _VIEW__RAM_FS_DIALOG_H_ + +#include +#include + +namespace Sculpt { struct Ram_fs_dialog; } + + +struct Sculpt::Ram_fs_dialog : Noncopyable, Dialog +{ + Storage_target const &_used_target; + + Storage_target const _target { "ram_fs", Partition::Number() }; + + Fs_dialog _fs_dialog; + + Selectable_item _operation_item { }; + Activatable_item _confirm_item { }; + + Hover_result hover(Xml_node hover) override + { + return any_hover_changed( + _fs_dialog.match_sub_dialog(hover), + _operation_item.match(hover, "button", "name"), + _confirm_item .match(hover, "button", "name")); + } + + void reset() override + { + _operation_item.reset(); + _confirm_item.reset(); + } + + struct Action : Interface, Noncopyable + { + virtual void reset_ram_fs() = 0; + }; + + void generate(Xml_generator &) const override { } + + void generate(Xml_generator &xml, Ram_fs_state const &ram_fs_state) const + { + _fs_dialog.generate(xml, ram_fs_state); + + if (!_used_target.ram_fs() && !ram_fs_state.inspected) { + xml.node("button", [&] () { + _operation_item.gen_button_attr(xml, "reset"); + + xml.node("label", [&] () { xml.attribute("text", "Reset ..."); }); + }); + + if (_operation_item.selected("reset")) { + xml.node("button", [&] () { + _confirm_item.gen_button_attr(xml, "confirm"); + xml.node("label", [&] () { xml.attribute("text", "Confirm"); }); + }); + } + } + } + + Click_result click(Fs_dialog::Action &fs_action) + { + if (_fs_dialog.click(fs_action) == Click_result::CONSUMED) + return Click_result::CONSUMED; + + /* toggle confirmation button when clicking on ram_fs reset */ + else if (_operation_item.hovered("reset")) + _operation_item.toggle_selection_on_click(); + + else if (_confirm_item.hovered("confirm")) + _confirm_item.propose_activation_on_click(); + + else return Click_result::IGNORED; + + return Click_result::CONSUMED; + } + + Clack_result clack(Action &action) + { + if (_confirm_item.hovered("confirm")) { + + _confirm_item.confirm_activation_on_clack(); + + if (_confirm_item.activated("confirm")) { + if (_operation_item.selected("reset")) { + action.reset_ram_fs(); + _operation_item.reset(); + _confirm_item.reset(); + return Clack_result::CONSUMED; + } + } + } else { + _confirm_item.reset(); + } + + return Clack_result::IGNORED; + } + + Ram_fs_dialog(Storage_target const &used_target) + : + _used_target(used_target), _fs_dialog(_target, used_target) + { } +}; + +#endif /* _VIEW__RAM_FS_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/settings_dialog.h b/repos/gems/src/app/sculpt_manager/view/settings_dialog.h new file mode 100644 index 000000000..e42ba1049 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/settings_dialog.h @@ -0,0 +1,107 @@ +/* + * \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 +#include + +namespace Sculpt { struct Settings_dialog; } + + +struct Sculpt::Settings_dialog : Noncopyable, Dialog +{ + Font_size const &_current_font_size; + + Hoverable_item _item { }; + + static Hoverable_item::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 Hoverable_item::Id(); + } + + Hover_result hover(Xml_node hover) override + { + return any_hover_changed( + _item.match(hover, "frame", "vbox", "hbox", "button", "name")); + } + + void reset() override { } + + struct Action : Interface, Noncopyable + { + virtual void select_font_size(Font_size) = 0; + }; + + void generate(Xml_generator &xml) const override + { + gen_named_node(xml, "frame", "network", [&] () { + xml.node("vbox", [&] () { + gen_named_node(xml, "hbox", "font_size", [&] () { + gen_named_node(xml, "label", "label", [&] () { + xml.attribute("text", " Font size "); }); + + auto gen_font_size_button = [&] (Font_size font_size) + { + Label const label = _font_size_id(font_size); + gen_named_node(xml, "button", label, [&] () { + _item.gen_hovered_attr(xml, label); + + if (font_size == _current_font_size) + xml.attribute("selected", true); + + xml.node("label", [&] () { + xml.attribute("text", label); }); + }); + }; + + gen_font_size_button(Font_size::SMALL); + gen_font_size_button(Font_size::MEDIUM); + gen_font_size_button(Font_size::LARGE); + }); + }); + }); + } + + Click_result click(Action &action) + { + Click_result result = Click_result::IGNORED; + + auto apply_font_size = [&] (Font_size font_size) + { + if (_item.hovered(_font_size_id(font_size))) { + action.select_font_size(font_size); + result = Click_result::CONSUMED; + } + }; + + apply_font_size(Font_size::SMALL); + apply_font_size(Font_size::MEDIUM); + apply_font_size(Font_size::LARGE); + + return result; + } + + Settings_dialog(Font_size const ¤t_font_size) + : + _current_font_size(current_font_size) + { } +}; + +#endif /* _VIEW__RAM_FS_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.cc b/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.cc new file mode 100644 index 000000000..70c543acf --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.cc @@ -0,0 +1,122 @@ +/* + * \brief Storage-device management dialog + * \author Norman Feske + * \date 2018-04-30 + */ + +/* + * 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 + +/* local includes */ +#include "storage_device_dialog.h" + +using namespace Sculpt; + + +void Storage_device_dialog::_gen_partition(Xml_generator &xml, + Storage_device const &device, + Partition const &partition) const +{ + bool const selected = _partition_item.selected(partition.number); + + gen_named_node(xml, "hbox", partition.number, [&] () { + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", "yes"); + xml.node("hbox", [&] () { + gen_named_node(xml, "button", "button", [&] () { + + if (_partition_item.hovered(partition.number)) + xml.attribute("hovered", "yes"); + + if (selected) + xml.attribute("selected", "yes"); + + xml.node("label", [&] () { xml.attribute("text", partition.number); }); + }); + + if (partition.label.length() > 1) + gen_named_node(xml, "label", "label", [&] () { + xml.attribute("text", String<80>(" (", partition.label, ") ")); }); + + Storage_target const target { device.label, partition.number }; + if (_used_target == target) + gen_named_node(xml, "label", "used", [&] () { xml.attribute("text", "* "); }); + }); + }); + + gen_named_node(xml, "float", "right", [&] () { + xml.attribute("east", "yes"); + xml.node("label", [&] () { + xml.attribute("text", String<64>(partition.capacity, " ")); }); + }); + }); + + if (selected && _partition_dialog.constructed()) + _partition_dialog->gen_operations(xml, device, partition); +} + + +void Storage_device_dialog::generate(Xml_generator &xml, Storage_device const &dev) const +{ + xml.node("frame", [&] () { + xml.attribute("name", dev.label); + xml.attribute("style", "invisible"); + xml.node("vbox", [&] () { + dev.partitions.for_each([&] (Partition const &partition) { + _gen_partition(xml, dev, partition); }); + + if (!_partition_item.any_selected() && _partition_dialog.constructed()) + _partition_dialog->gen_operations(xml, dev, *dev.whole_device_partition); + }); + }); +} + + +Dialog::Hover_result Storage_device_dialog::hover(Xml_node hover) +{ + Hover_result result = Hover_result::UNMODIFIED; + + if (_partition_dialog.constructed() && + _partition_dialog->match_sub_dialog(hover, "frame", "vbox") == Hover_result::CHANGED) + result = Hover_result::CHANGED; + + return any_hover_changed( + result, + _partition_item.match(hover, "frame", "vbox", "hbox", "name")); +} + + +Dialog::Click_result Storage_device_dialog::click(Action &action) +{ + Storage_target const orig_selected_target = _selected_storage_target(); + + _partition_item.toggle_selection_on_click(); + + if (_selected_storage_target() != orig_selected_target) { + _partition_dialog.construct(_selected_storage_target(), + _storage_devices, _used_target); + return Click_result::CONSUMED; + } + + if (_partition_dialog.constructed()) + return _partition_dialog->click(action); + + return Click_result::IGNORED; +} + + +Dialog::Clack_result Storage_device_dialog::clack(Action &action) +{ + if (_partition_dialog.constructed()) + return _partition_dialog->clack(action); + + return Clack_result::IGNORED; +} + diff --git a/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.h b/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.h new file mode 100644 index 000000000..801859b0b --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/storage_device_dialog.h @@ -0,0 +1,78 @@ +/* + * \brief Storage-device management dialog + * \author Norman Feske + * \date 2020-01-29 + */ + +/* + * 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__STORAGE_DEVICE_DIALOG_H_ +#define _VIEW__STORAGE_DEVICE_DIALOG_H_ + +#include +#include +#include +#include +#include +#include + +namespace Sculpt { struct Storage_device_dialog; } + + +struct Sculpt::Storage_device_dialog : Dialog +{ + Storage_device::Label const _device; + Storage_devices const &_storage_devices; + Storage_target const &_used_target; + + Selectable_item _partition_item { }; + + Reconstructible _partition_dialog { + _selected_storage_target(), _storage_devices, _used_target }; + + void generate(Xml_generator &) const override { } + + void _gen_partition(Xml_generator &, Storage_device const &, Partition const &) const; + + void generate(Xml_generator &, Storage_device const &) const; + + Hover_result hover(Xml_node hover) override; + + using Action = Partition_dialog::Action; + + Storage_target _selected_storage_target() const + { + Partition::Number partition = (_partition_item._selected == "") + ? Partition::Number { } + : Partition::Number(_partition_item._selected); + + return Storage_target { _device, partition }; + } + + void reset() override { } + + void reset_operation() + { + if (_partition_dialog.constructed()) + _partition_dialog->reset_operation(); + } + + Click_result click(Action &action); + Clack_result clack(Action &action); + + Storage_device_dialog(Storage_device::Label const &device, + Storage_devices const &storage_devices, + Storage_target const &used) + : + _device(device), + _storage_devices(storage_devices), + _used_target(used) + { } +}; + +#endif /* _VIEW__STORAGE_DEVICE_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/storage_dialog.cc b/repos/gems/src/app/sculpt_manager/view/storage_dialog.cc index d0d879594..1ce48bb53 100644 --- a/repos/gems/src/app/sculpt_manager/view/storage_dialog.cc +++ b/repos/gems/src/app/sculpt_manager/view/storage_dialog.cc @@ -17,213 +17,11 @@ /* local includes */ #include "storage_dialog.h" - -void Sculpt::Storage_dialog::_gen_partition_operations(Xml_generator &xml, - Storage_device const &device, - Partition const &partition) const -{ - Storage_target const target { device.label, partition.number }; - - String<16> const version(device.label, ".", partition.number); - - bool const whole_device = !partition.number.valid(); - - bool const device_in_use = (_used_target.device == device.label); - - bool const target_in_use = (_used_target == target) - || (whole_device && device_in_use) - || partition.file_system_inspected; - - bool const relabel_in_progress = device.relabel_in_progress(); - - bool const expand_in_progress = device.expand_in_progress(); - - bool const format_selected = _operation_item.selected("format"); - - bool const expand_selected = _operation_item.selected("expand"); - - if (partition.file_system.accessible() && !format_selected - && !expand_selected && !expand_in_progress) { - - if (!partition.check_in_progress && !partition.format_in_progress - && partition.file_system.accessible() && !relabel_in_progress) { - - xml.node("button", [&] () { - _inspect_item.gen_button_attr(xml, "browse"); - xml.attribute("version", version); - - if (partition.file_system_inspected) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { xml.attribute("text", "Inspect"); }); - }); - } - - if ((!_used_target.valid() || _used_target == target) - && !partition.check_in_progress && !partition.format_in_progress - && !relabel_in_progress) { - - xml.node("button", [&] () { - xml.attribute("version", version); - _use_item.gen_button_attr(xml, "use"); - if (_used_target == target) - xml.attribute("selected", "yes"); - xml.node("label", [&] () { xml.attribute("text", "Use"); }); - }); - } - - if ((device.all_partitions_idle() || partition.relabel_in_progress()) - && partition.genode() && !device_in_use) { - - xml.node("button", [&] () { - - /* support hovering only if no relabeling is in progress */ - if (partition.relabel_in_progress()) - xml.attribute("name", "relabel"); - else - _relabel_item.gen_button_attr(xml, "relabel"); - - xml.attribute("version", version); - - if (partition.genode_default() || partition.relabel_in_progress()) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { - xml.attribute("text", "Default"); }); - }); - if (partition.relabel_in_progress()) - xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); - } - - if (!target_in_use && !partition.format_in_progress && partition.checkable() - && !relabel_in_progress) { - - xml.node("button", [&] () { - _operation_item.gen_button_attr(xml, "check"); - xml.attribute("version", version); - - if (partition.check_in_progress) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { xml.attribute("text", "Check"); }); - }); - if (partition.check_in_progress) - xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); - } - } - - bool const whole_device_with_partition_in_use = - whole_device && !device.all_partitions_idle(); - - bool const format_button_visible = !target_in_use - && !whole_device_with_partition_in_use - && !partition.check_in_progress - && !expand_in_progress - && !relabel_in_progress - && !_operation_item.selected("expand"); - - bool const expand_button_visible = !target_in_use - && !whole_device - && !partition.check_in_progress - && !partition.format_in_progress - && !relabel_in_progress - && partition.expandable() - && !_operation_item.selected("format"); - - bool const progress_msg_visible = - (_operation_item.selected("format") && partition.format_in_progress) - || (_operation_item.selected("expand") && partition.expand_in_progress()); - - bool const confirm_visible = - (_operation_item.selected("format") && !partition.format_in_progress) - || (_operation_item.selected("expand") && !partition.expand_in_progress()); - - if (format_button_visible) { - xml.node("button", [&] () { - _operation_item.gen_button_attr(xml, "format"); - xml.attribute("version", version); - - if (partition.format_in_progress) - xml.attribute("selected", "yes"); - - if (whole_device) { - xml.node("label", [&] () { xml.attribute("text", "Format device ..."); }); - } else { - xml.node("label", [&] () { xml.attribute("text", "Format partition ..."); }); - } - }); - } - - if (expand_button_visible) { - xml.node("button", [&] () { - _operation_item.gen_button_attr(xml, "expand"); - xml.attribute("version", version); - - if (partition.expand_in_progress()) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { xml.attribute("text", "Expand ..."); }); - }); - } - - if (progress_msg_visible) - xml.node("label", [&] () { xml.attribute("text", "In progress..."); }); - - if (confirm_visible) { - xml.node("button", [&] () { - _confirm_item.gen_button_attr(xml, "confirm"); - xml.attribute("version", version); - xml.node("label", [&] () { xml.attribute("text", "Confirm"); }); - }); - } -} +using namespace Sculpt; -void Sculpt::Storage_dialog::_gen_partition(Xml_generator &xml, - Storage_device const &device, - Partition const &partition) const -{ - bool const selected = _partition_item.selected(partition.number); - - gen_named_node(xml, "hbox", partition.number, [&] () { - gen_named_node(xml, "float", "left", [&] () { - xml.attribute("west", "yes"); - xml.node("hbox", [&] () { - gen_named_node(xml, "button", "button", [&] () { - - if (_partition_item.hovered(partition.number)) - xml.attribute("hovered", "yes"); - - if (selected) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { xml.attribute("text", partition.number); }); - }); - - if (partition.label.length() > 1) - gen_named_node(xml, "label", "label", [&] () { - xml.attribute("text", String<80>(" (", partition.label, ") ")); }); - - Storage_target const target { device.label, partition.number }; - if (_used_target == target) - gen_named_node(xml, "label", "used", [&] () { xml.attribute("text", "* "); }); - }); - }); - - gen_named_node(xml, "float", "right", [&] () { - xml.attribute("east", "yes"); - xml.node("label", [&] () { - xml.attribute("text", String<64>(partition.capacity, " ")); }); - }); - }); - - if (selected) - _gen_partition_operations(xml, device, partition); -} - - -void Sculpt::Storage_dialog::_gen_block_device(Xml_generator &xml, - Block_device const &dev) const +void Storage_dialog::_gen_block_device(Xml_generator &xml, + Block_device const &dev) const { bool const selected = _device_item.selected(dev.label); @@ -257,23 +55,13 @@ void Sculpt::Storage_dialog::_gen_block_device(Xml_generator &xml, }); }); - if (selected) { - xml.node("frame", [&] () { - xml.attribute("name", dev.label); - xml.node("vbox", [&] () { - dev.partitions.for_each([&] (Partition const &partition) { - _gen_partition(xml, dev, partition); }); - - if (!_partition_item.any_selected()) - _gen_partition_operations(xml, dev, *dev.whole_device_partition); - }); - }); - } + if (_storage_device_dialog.constructed() && selected) + _storage_device_dialog->generate(xml, dev); } -void Sculpt::Storage_dialog::_gen_usb_storage_device(Xml_generator &xml, - Usb_storage_device const &dev) const +void Storage_dialog::_gen_usb_storage_device(Xml_generator &xml, + Usb_storage_device const &dev) const { bool const discarded = dev.discarded(); bool const selected = !discarded && _device_item.selected(dev.label); @@ -290,9 +78,7 @@ void Sculpt::Storage_dialog::_gen_usb_storage_device(Xml_generator &x xml.node("hbox", [&] () { gen_named_node(xml, "float", "info", [&] () { xml.attribute("west", "yes"); - xml.node("hbox", [&] () { - gen_named_node(xml, "label", "device", [&] () { xml.attribute("text", dev.label); }); @@ -309,7 +95,8 @@ void Sculpt::Storage_dialog::_gen_usb_storage_device(Xml_generator &x }); typedef String<64> Info; - Info const info = dev.discarded() ? Info("unsupported") : Info(dev.capacity); + Info const info = dev.discarded() ? Info(" unsupported") + : Info(" ", dev.capacity); gen_named_node(xml, "float", "capacity", [&] () { xml.attribute("east", "yes"); @@ -318,224 +105,68 @@ void Sculpt::Storage_dialog::_gen_usb_storage_device(Xml_generator &x }); }); - if (selected) - xml.node("frame", [&] () { - xml.attribute("name", dev.label); - xml.node("vbox", [&] () { - dev.partitions.for_each([&] (Partition const &partition) { - _gen_partition(xml, dev, partition); }); - - if (!_partition_item.any_selected()) - _gen_partition_operations(xml, dev, *dev.whole_device_partition); - }); - }); + if (_storage_device_dialog.constructed() && selected) + _storage_device_dialog->generate(xml, dev); } -void Sculpt::Storage_dialog::_gen_ram_fs(Xml_generator &xml) const +void Storage_dialog::gen_block_devices(Xml_generator &xml) const { - bool const selected = _device_item.selected("ram_fs"); - - gen_named_node(xml, "button", "ram_fs", [&] () { - - if (_device_item.hovered("ram_fs")) - xml.attribute("hovered", "yes"); - - if (selected) - xml.attribute("selected", "yes"); - - xml.node("hbox", [&] () { - gen_named_node(xml, "float", "info", [&] () { - xml.attribute("west", "yes"); - - xml.node("hbox", [&] () { - - gen_named_node(xml, "label", "device", [&] () { - xml.attribute("text", "ram (in-memory file system) "); }); - - if (_used_target.device == "ram_fs") - gen_named_node(xml, "label", "used", [&] () { - xml.attribute("text", "* "); }); - }); - }); - - gen_named_node(xml, "float", "capacity", [&] () { - xml.attribute("east", "yes"); - xml.node("label", [&] () { - Capacity const capacity { _ram_fs_state.ram_quota().value }; - xml.attribute("text", String<64>(capacity)); }); }); - }); - }); - - if (selected) { - xml.node("frame", [&] () { - xml.attribute("name", "ram_fs_operations"); - xml.node("vbox", [&] () { - - xml.node("button", [&] () { - _inspect_item.gen_button_attr(xml, "browse"); - - if (_ram_fs_state.inspected) - xml.attribute("selected", "yes"); - - xml.node("label", [&] () { xml.attribute("text", "Inspect"); }); - }); - - if (!_used_target.valid() || _used_target.ram_fs()) { - xml.node("button", [&] () { - _use_item.gen_button_attr(xml, "use"); - if (_used_target.ram_fs()) - xml.attribute("selected", "yes"); - xml.node("label", [&] () { xml.attribute("text", "Use"); }); - }); - } - - if (!_used_target.ram_fs() && !_ram_fs_state.inspected) { - xml.node("button", [&] () { - _operation_item.gen_button_attr(xml, "reset"); - - xml.node("label", [&] () { xml.attribute("text", "Reset ..."); }); - }); - - if (_operation_item.selected("reset")) { - xml.node("button", [&] () { - _confirm_item.gen_button_attr(xml, "confirm"); - xml.node("label", [&] () { xml.attribute("text", "Confirm"); }); - }); - } - } - }); - }); - } + _storage_devices.block_devices.for_each([&] (Block_device const &dev) { + _gen_block_device(xml, dev); }); } -void Sculpt::Storage_dialog::generate(Xml_generator &xml, bool expanded) const +void Storage_dialog::gen_usb_storage_devices(Xml_generator &xml) const { - gen_named_node(xml, "frame", "storage", [&] () { - xml.node("vbox", [&] () { - gen_named_node(xml, "label", "title", [&] () { - xml.attribute("text", "Storage"); - xml.attribute("font", "title/regular"); - }); - - if (!expanded) - return; - - _storage_devices.block_devices.for_each([&] (Block_device const &dev) { - _gen_block_device(xml, dev); }); - _storage_devices.usb_storage_devices.for_each([&] (Usb_storage_device const &dev) { - _gen_usb_storage_device(xml, dev); }); - - _gen_ram_fs(xml); - }); - }); + _storage_devices.usb_storage_devices.for_each([&] (Usb_storage_device const &dev) { + _gen_usb_storage_device(xml, dev); }); } -void Sculpt::Storage_dialog::hover(Xml_node hover) +Dialog::Hover_result Storage_dialog::hover(Xml_node hover) { - bool const changed = - _device_item .match(hover, "vbox", "button", "name") | - _partition_item.match(hover, "vbox", "frame", "vbox", "hbox", "name") | - _use_item .match(hover, "vbox", "frame", "vbox", "button", "name") | - _relabel_item .match(hover, "vbox", "frame", "vbox", "button", "name") | - _inspect_item .match(hover, "vbox", "frame", "vbox", "button", "name") | - _operation_item.match(hover, "vbox", "frame", "vbox", "button", "name") | - _confirm_item .match(hover, "vbox", "frame", "vbox", "button", "name"); + Hover_result result = Hover_result::UNMODIFIED; - if (changed) - _dialog_generator.generate_dialog(); + if (_storage_device_dialog.constructed() && + _storage_device_dialog->hover(hover) == Hover_result::CHANGED) + result = Hover_result::CHANGED; + + return any_hover_changed(result, _device_item.match(hover, "button", "name")); } -void Sculpt::Storage_dialog::click(Action &action) +Dialog::Click_result Storage_dialog::click(Action &action) { - Selectable_item::Id const old_selected_device = _device_item._selected; - Selectable_item::Id const old_selected_partition = _partition_item._selected; + Selectable_item::Id const old_selected_device = _device_item._selected; _device_item.toggle_selection_on_click(); - _partition_item.toggle_selection_on_click(); - if (!_device_item.selected(old_selected_device)) - _partition_item.reset(); + if (old_selected_device != _device_item._selected) { - if (!_device_item.selected(old_selected_device) - || !_partition_item.selected(old_selected_partition)) - reset_operation(); - - Storage_target const target = _selected_storage_target(); - - if (_operation_item.hovered("format")) { - if (_operation_item.selected("format")) - action.cancel_format(_selected_storage_target()); - else - _operation_item.toggle_selection_on_click(); + _storage_device_dialog.destruct(); + _storage_device_dialog.conditional(_device_item.any_selected(), + _device_item._selected, + _storage_devices, _used_target); + return Click_result::CONSUMED; } - if (_operation_item.hovered("expand")) { - if (_operation_item.selected("expand")) - action.cancel_expand(_selected_storage_target()); - else - _operation_item.toggle_selection_on_click(); + if (_storage_device_dialog.constructed() + && _storage_device_dialog->click(action) == Click_result::CONSUMED) { + + return Click_result::CONSUMED; } - /* toggle confirmation button when clicking on ram_fs reset */ - if (_operation_item.hovered("reset")) - _operation_item.toggle_selection_on_click(); - - if (_operation_item.hovered("check")) - action.check(target); - - /* toggle file browser */ - if (_inspect_item.hovered("browse")) - action.toggle_file_browser(target); - - if (_use_item.hovered("use")) { - - /* release currently used device */ - if (target.valid() && target == _used_target) - action.use(Storage_target{}); - - /* select new used device if none is defined */ - else if (!_used_target.valid()) - action.use(target); - } - - if (_relabel_item.hovered("relabel")) - action.toggle_default_storage_target(target); - - if (_confirm_item.hovered("confirm")) { - _confirm_item.propose_activation_on_click(); - } - - _dialog_generator.generate_dialog(); + return Click_result::IGNORED; } -void Sculpt::Storage_dialog::clack(Action &action) +Dialog::Clack_result Storage_dialog::clack(Action &action) { - if (_confirm_item.hovered("confirm")) { + if (_storage_device_dialog.constructed() + && _storage_device_dialog->clack(action) == Clack_result::CONSUMED) + return Clack_result::CONSUMED; - _confirm_item.confirm_activation_on_clack(); - - if (_confirm_item.activated("confirm")) { - - Storage_target const target = _selected_storage_target(); - - if (_operation_item.selected("format")) - action.format(target); - - if (_operation_item.selected("expand")) - action.expand(target); - - if (target.ram_fs() && _operation_item.selected("reset")) - action.reset_ram_fs(); - } - } else { - _confirm_item.reset(); - } - - _dialog_generator.generate_dialog(); + return Clack_result::IGNORED; } diff --git a/repos/gems/src/app/sculpt_manager/view/storage_dialog.h b/repos/gems/src/app/sculpt_manager/view/storage_dialog.h index 24c5639d1..c2968d49b 100644 --- a/repos/gems/src/app/sculpt_manager/view/storage_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/storage_dialog.h @@ -17,98 +17,51 @@ #include #include #include -#include #include -#include -#include +#include namespace Sculpt { struct Storage_dialog; } struct Sculpt::Storage_dialog : Dialog { - Env &_env; - - Dialog::Generator &_dialog_generator; - Storage_devices const &_storage_devices; - Ram_fs_state const &_ram_fs_state; - - Selectable_item _device_item { }; - Selectable_item _partition_item { }; - Hoverable_item _use_item { }; - Hoverable_item _relabel_item { }; - Hoverable_item _inspect_item { }; - Selectable_item _operation_item { }; - Activatable_item _confirm_item { }; - - void generate(Xml_generator &, bool expanded) const; - - void _gen_block_device(Xml_generator &, Block_device const &) const; - - void _gen_partition(Xml_generator &, Storage_device const &, Partition const &) const; - - void _gen_partition_operations(Xml_generator &, Storage_device const &, Partition const &) const; - - void _gen_usb_storage_device(Xml_generator &, Usb_storage_device const &) const; - - void _gen_ram_fs(Xml_generator &) const; - - void hover(Xml_node hover) override; + Selectable_item _device_item { }; Storage_target const &_used_target; - struct Action : Interface - { - virtual void format(Storage_target const &) = 0; + Constructible _storage_device_dialog { }; - virtual void cancel_format(Storage_target const &) = 0; + void _gen_block_device (Xml_generator &, Block_device const &) const; + void _gen_usb_storage_device(Xml_generator &, Usb_storage_device const &) const; - virtual void expand(Storage_target const &) = 0; + void generate(Xml_generator &) const override { }; - virtual void cancel_expand(Storage_target const &) = 0; + void gen_block_devices (Xml_generator &) const; + void gen_usb_storage_devices(Xml_generator &) const; - virtual void check(Storage_target const &) = 0; + Hover_result hover(Xml_node hover) override; - virtual void toggle_file_browser(Storage_target const &) = 0; + using Action = Storage_device_dialog::Action; - virtual void toggle_default_storage_target(Storage_target const &) = 0; - - virtual void use(Storage_target const &) = 0; - - virtual void reset_ram_fs() = 0; - }; - - Storage_target _selected_storage_target() const - { - Partition::Number partition = (_partition_item._selected == "") - ? Partition::Number { } - : Partition::Number(_partition_item._selected); - - return Storage_target { _device_item._selected, partition }; - } + void reset() override { } void reset_operation() { - _operation_item.reset(); - _confirm_item.reset(); - - _dialog_generator.generate_dialog(); + if (_storage_device_dialog.constructed()) + _storage_device_dialog->reset_operation(); } - void click(Action &action); + Click_result click(Action &); + Clack_result clack(Action &); - void clack(Action &action); - - Storage_dialog(Env &env, Dialog::Generator &dialog_generator, - Storage_devices const &storage_devices, - Ram_fs_state const &ram_fs_state, Storage_target const &used) + Storage_dialog(Storage_devices const &storage_devices, + Storage_target const &used) : - _env(env), _dialog_generator(dialog_generator), - _storage_devices(storage_devices), _ram_fs_state(ram_fs_state), + _storage_devices(storage_devices), _used_target(used) { } }; -#endif /* _STORAGE_DIALOG_H_ */ +#endif /* _VIEW__STORAGE_DIALOG_H_ */