mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 00:24:51 +00:00
sculpt: redesigned popup dialog
The new popup dialog mirrors the concept of the software add and option dialogs of the phone version. Fixes #5168
This commit is contained in:
parent
9ea99a896a
commit
268a77add1
@ -915,9 +915,14 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
&& _software_tabs_widget.hosted.options_selected()
|
&& _software_tabs_widget.hosted.options_selected()
|
||||||
&& _storage._selected_target.valid());
|
&& _storage._selected_target.valid());
|
||||||
|
|
||||||
s.widget(_software_add_widget, _software_title_bar.selected()
|
{
|
||||||
&& _software_tabs_widget.hosted.add_selected()
|
using Attr = Software_add_widget::Attr;
|
||||||
&& _storage._selected_target.valid());
|
s.widget(_software_add_widget, _software_title_bar.selected()
|
||||||
|
&& _software_tabs_widget.hosted.add_selected()
|
||||||
|
&& _storage._selected_target.valid(),
|
||||||
|
Attr { .visible_frames = true,
|
||||||
|
.left_aligned_items = false });
|
||||||
|
}
|
||||||
|
|
||||||
_image_index_rom.with_xml([&] (Xml_node const &image_index) {
|
_image_index_rom.with_xml([&] (Xml_node const &image_index) {
|
||||||
s.widget(_software_update_widget, _software_title_bar.selected()
|
s.widget(_software_update_widget, _software_title_bar.selected()
|
||||||
|
@ -18,17 +18,19 @@
|
|||||||
#include <os/reporter.h>
|
#include <os/reporter.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <model/wpa_passphrase.h>
|
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <model/popup.h>
|
||||||
|
#include <model/wpa_passphrase.h>
|
||||||
#include <view/network_widget.h>
|
#include <view/network_widget.h>
|
||||||
#include <view/panel_dialog.h>
|
#include <view/panel_dialog.h>
|
||||||
#include <view/system_dialog.h>
|
#include <view/system_dialog.h>
|
||||||
|
#include <view/popup_dialog.h>
|
||||||
|
|
||||||
namespace Sculpt { struct Keyboard_focus; }
|
namespace Sculpt { struct Keyboard_focus; }
|
||||||
|
|
||||||
struct Sculpt::Keyboard_focus
|
struct Sculpt::Keyboard_focus
|
||||||
{
|
{
|
||||||
enum Target { INITIAL, WPA_PASSPHRASE, SYSTEM_DIALOG, WM } target { INITIAL };
|
enum Target { UNDEFINED, WPA_PASSPHRASE, SYSTEM_DIALOG, POPUP, WM } target { UNDEFINED };
|
||||||
|
|
||||||
Expanding_reporter _focus_reporter;
|
Expanding_reporter _focus_reporter;
|
||||||
|
|
||||||
@ -36,13 +38,21 @@ struct Sculpt::Keyboard_focus
|
|||||||
Wpa_passphrase &_wpa_passphrase;
|
Wpa_passphrase &_wpa_passphrase;
|
||||||
Panel_dialog::State const &_panel;
|
Panel_dialog::State const &_panel;
|
||||||
System_dialog const &_system_dialog;
|
System_dialog const &_system_dialog;
|
||||||
|
Popup_dialog const &_popup_dialog;
|
||||||
bool const &_system_visible;
|
bool const &_system_visible;
|
||||||
|
Popup const &_popup;
|
||||||
|
|
||||||
void update()
|
void update()
|
||||||
{
|
{
|
||||||
Target const orig_target = target;
|
Target const orig_target = target;
|
||||||
|
|
||||||
target = WM;
|
target = UNDEFINED;
|
||||||
|
|
||||||
|
if (_panel.inspect_tab_visible())
|
||||||
|
target = WM;
|
||||||
|
|
||||||
|
if ((_popup.state == Popup::VISIBLE) && _popup_dialog.keyboard_needed())
|
||||||
|
target = POPUP;
|
||||||
|
|
||||||
if (_panel.network_visible() && _network_widget.need_keyboard_focus_for_passphrase())
|
if (_panel.network_visible() && _network_widget.need_keyboard_focus_for_passphrase())
|
||||||
target = WPA_PASSPHRASE;
|
target = WPA_PASSPHRASE;
|
||||||
@ -61,10 +71,13 @@ struct Sculpt::Keyboard_focus
|
|||||||
|
|
||||||
case WPA_PASSPHRASE:
|
case WPA_PASSPHRASE:
|
||||||
case SYSTEM_DIALOG:
|
case SYSTEM_DIALOG:
|
||||||
|
case POPUP:
|
||||||
xml.attribute("label", "manager -> input");
|
xml.attribute("label", "manager -> input");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INITIAL:
|
case UNDEFINED:
|
||||||
|
break;
|
||||||
|
|
||||||
case WM:
|
case WM:
|
||||||
xml.attribute("label", "wm -> ");
|
xml.attribute("label", "wm -> ");
|
||||||
break;
|
break;
|
||||||
@ -77,14 +90,18 @@ struct Sculpt::Keyboard_focus
|
|||||||
Wpa_passphrase &wpa_passphrase,
|
Wpa_passphrase &wpa_passphrase,
|
||||||
Panel_dialog::State const &panel,
|
Panel_dialog::State const &panel,
|
||||||
System_dialog const &system_dialog,
|
System_dialog const &system_dialog,
|
||||||
bool const &system_visible)
|
bool const &system_visible,
|
||||||
|
Popup_dialog const &popup_dialog,
|
||||||
|
Popup const &popup)
|
||||||
:
|
:
|
||||||
_focus_reporter(env, "focus", "focus"),
|
_focus_reporter(env, "focus", "focus"),
|
||||||
_network_widget(network_widget),
|
_network_widget(network_widget),
|
||||||
_wpa_passphrase(wpa_passphrase),
|
_wpa_passphrase(wpa_passphrase),
|
||||||
_panel(panel),
|
_panel(panel),
|
||||||
_system_dialog(system_dialog),
|
_system_dialog(system_dialog),
|
||||||
_system_visible(system_visible)
|
_popup_dialog(popup_dialog),
|
||||||
|
_system_visible(system_visible),
|
||||||
|
_popup(popup)
|
||||||
{
|
{
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -64,16 +64,15 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
Network::Info,
|
Network::Info,
|
||||||
Graph::Action,
|
Graph::Action,
|
||||||
Panel_dialog::Action,
|
Panel_dialog::Action,
|
||||||
Popup_dialog::Action,
|
|
||||||
Network_widget::Action,
|
Network_widget::Action,
|
||||||
Settings_widget::Action,
|
Settings_widget::Action,
|
||||||
Software_presets_widget::Action,
|
Software_presets_widget::Action,
|
||||||
Software_update_widget::Action,
|
Software_update_widget::Action,
|
||||||
File_browser_dialog::Action,
|
File_browser_dialog::Action,
|
||||||
Popup_dialog::Construction_info,
|
Popup_dialog::Action,
|
||||||
|
Component::Construction_info,
|
||||||
Depot_query,
|
Depot_query,
|
||||||
Panel_dialog::State,
|
Panel_dialog::State,
|
||||||
Popup_dialog::Refresh,
|
|
||||||
Screensaver::Action,
|
Screensaver::Action,
|
||||||
Drivers::Info,
|
Drivers::Info,
|
||||||
Drivers::Action
|
Drivers::Action
|
||||||
@ -414,6 +413,8 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
|
|
||||||
Depot::Archive::User _image_index_user = _build_info.depot_user;
|
Depot::Archive::User _image_index_user = _build_info.depot_user;
|
||||||
|
|
||||||
|
Depot::Archive::User _index_user = _build_info.depot_user;
|
||||||
|
|
||||||
Expanding_reporter _depot_query_reporter { _env, "query", "depot_query"};
|
Expanding_reporter _depot_query_reporter { _env, "query", "depot_query"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -442,13 +443,19 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
xml.attribute("arch", _deploy._arch);
|
xml.attribute("arch", _deploy._arch);
|
||||||
xml.attribute("version", _query_version.value);
|
xml.attribute("version", _query_version.value);
|
||||||
|
|
||||||
if (_popup_dialog.depot_query_needs_users())
|
bool const query_users = _popup_dialog.watches_depot()
|
||||||
|
|| _system_dialog_watches_depot()
|
||||||
|
|| !_scan_rom.valid();
|
||||||
|
if (query_users)
|
||||||
xml.node("scan", [&] {
|
xml.node("scan", [&] {
|
||||||
xml.attribute("users", "yes"); });
|
xml.attribute("users", "yes"); });
|
||||||
|
|
||||||
if (_system_dialog_watches_depot() || !_scan_rom.valid())
|
if (_popup_dialog.watches_depot() || !_image_index_rom.valid())
|
||||||
xml.node("scan", [&] {
|
xml.node("index", [&] {
|
||||||
xml.attribute("users", "yes"); });
|
xml.attribute("user", _index_user);
|
||||||
|
xml.attribute("version", _sculpt_version);
|
||||||
|
xml.attribute("content", "yes");
|
||||||
|
});
|
||||||
|
|
||||||
if (_system_dialog_watches_depot() || !_image_index_rom.valid())
|
if (_system_dialog_watches_depot() || !_image_index_rom.valid())
|
||||||
xml.node("image_index", [&] {
|
xml.node("image_index", [&] {
|
||||||
@ -457,8 +464,9 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
xml.attribute("user", _image_index_user);
|
xml.attribute("user", _image_index_user);
|
||||||
});
|
});
|
||||||
|
|
||||||
_scan_rom.with_xml([&] (Xml_node const &scan) {
|
_runtime_state.with_construction([&] (Component const &component) {
|
||||||
_popup_dialog.gen_depot_query(xml, scan); });
|
xml.node("blueprint", [&] {
|
||||||
|
xml.attribute("pkg", component.path); }); });
|
||||||
|
|
||||||
/* update query for blueprints of all unconfigured start nodes */
|
/* update query for blueprints of all unconfigured start nodes */
|
||||||
_deploy.gen_depot_query(xml);
|
_deploy.gen_depot_query(xml);
|
||||||
@ -482,6 +490,29 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************
|
||||||
|
** Browse index **
|
||||||
|
******************/
|
||||||
|
|
||||||
|
Rom_handler<Main> _index_rom {
|
||||||
|
_env, "report -> runtime/depot_query/index", *this, &Main::_handle_index };
|
||||||
|
|
||||||
|
void _handle_index(Xml_node const &)
|
||||||
|
{
|
||||||
|
if (_popup_dialog.watches_depot())
|
||||||
|
_popup_dialog.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Software_add_widget::Action interface
|
||||||
|
*/
|
||||||
|
void query_index(Depot::Archive::User const &user) override
|
||||||
|
{
|
||||||
|
_index_user = user;
|
||||||
|
trigger_depot_query();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
** Blueprint query **
|
** Blueprint query **
|
||||||
*********************/
|
*********************/
|
||||||
@ -502,9 +533,10 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_runtime_state.apply_to_construction([&] (Component &component) {
|
_runtime_state.apply_to_construction([&] (Component &component) {
|
||||||
_popup_dialog.apply_blueprint(component, blueprint); });
|
component.try_apply_blueprint(blueprint); });
|
||||||
|
|
||||||
_deploy.handle_deploy();
|
_deploy.handle_deploy();
|
||||||
|
_popup_dialog.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -519,8 +551,9 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
|
|
||||||
void _handle_scan(Xml_node const &)
|
void _handle_scan(Xml_node const &)
|
||||||
{
|
{
|
||||||
_popup_dialog.depot_users_scan_updated();
|
|
||||||
_system_dialog.sanitize_user_selection();
|
_system_dialog.sanitize_user_selection();
|
||||||
|
_popup_dialog.sanitize_user_selection();
|
||||||
|
_popup_dialog.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rom_handler<Main> _image_index_rom {
|
Rom_handler<Main> _image_index_rom {
|
||||||
@ -734,7 +767,8 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
****************************/
|
****************************/
|
||||||
|
|
||||||
Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase,
|
Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase,
|
||||||
*this, _system_dialog, _system_visible };
|
*this, _system_dialog, _system_visible,
|
||||||
|
_popup_dialog, _popup };
|
||||||
|
|
||||||
struct Keyboard_focus_guard
|
struct Keyboard_focus_guard
|
||||||
{
|
{
|
||||||
@ -786,14 +820,18 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
ev.handle_press([&] (Input::Keycode, Codepoint code) {
|
ev.handle_press([&] (Input::Keycode, Codepoint code) {
|
||||||
if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|
if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|
||||||
_network.handle_key_press(code);
|
_network.handle_key_press(code);
|
||||||
|
if (_keyboard_focus.target == Keyboard_focus::POPUP)
|
||||||
|
_popup_dialog.handle_key(code, *this);
|
||||||
else if (_system_visible && _system_dialog.keyboard_needed())
|
else if (_system_visible && _system_dialog.keyboard_needed())
|
||||||
_system_dialog.handle_key(code, *this);
|
_system_dialog.handle_key(code, *this);
|
||||||
|
|
||||||
need_generate_dialog = true;
|
need_generate_dialog = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (need_generate_dialog)
|
if (need_generate_dialog) {
|
||||||
_generate_dialog();
|
_generate_dialog();
|
||||||
|
_popup_dialog.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -996,6 +1034,41 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
generate_runtime_config();
|
generate_runtime_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Software_add_dialog::Action interface
|
||||||
|
*/
|
||||||
|
void update_sculpt_index(Depot::Archive::User const &user, Verify verify) override
|
||||||
|
{
|
||||||
|
_download_queue.remove_inactive_downloads();
|
||||||
|
_index_update_queue.remove_inactive_updates();
|
||||||
|
_index_update_queue.add(Path(user, "/index/", _sculpt_version), verify);
|
||||||
|
generate_runtime_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup_options_widget::Action interface
|
||||||
|
*/
|
||||||
|
void enable_optional_component(Path const &launcher) override
|
||||||
|
{
|
||||||
|
_runtime_state.launch(launcher, launcher);
|
||||||
|
|
||||||
|
/* trigger change of the deployment */
|
||||||
|
_deploy.update_managed_deploy_config();
|
||||||
|
_download_queue.remove_inactive_downloads();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup_options_widget::Action interface
|
||||||
|
*/
|
||||||
|
void disable_optional_component(Path const &launcher) override
|
||||||
|
{
|
||||||
|
_runtime_state.abandon(launcher);
|
||||||
|
|
||||||
|
/* update config/managed/deploy with the component 'name' removed */
|
||||||
|
_deploy.update_managed_deploy_config();
|
||||||
|
_download_queue.remove_inactive_downloads();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Panel::Action interface
|
* Panel::Action interface
|
||||||
*/
|
*/
|
||||||
@ -1254,7 +1327,6 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
{
|
{
|
||||||
/* close popup menu */
|
/* close popup menu */
|
||||||
_popup.state = Popup::OFF;
|
_popup.state = Popup::OFF;
|
||||||
_popup_dialog.reset();
|
|
||||||
_popup_dialog.refresh();
|
_popup_dialog.refresh();
|
||||||
|
|
||||||
/* remove popup window from window layout */
|
/* remove popup window from window layout */
|
||||||
@ -1264,31 +1336,32 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
_graph_view.refresh();
|
_graph_view.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void new_construction(Component::Path const &pkg, Verify verify,
|
||||||
* Popup_dialog::Action interface
|
Component::Info const &info) override
|
||||||
*/
|
|
||||||
void launch_global(Path const &launcher) override
|
|
||||||
{
|
{
|
||||||
_runtime_state.launch(launcher, launcher);
|
_runtime_state.new_construction(pkg, verify, info, _affinity_space);
|
||||||
|
trigger_depot_query();
|
||||||
_close_popup_dialog();
|
|
||||||
|
|
||||||
/* trigger change of the deployment */
|
|
||||||
_download_queue.remove_inactive_downloads();
|
|
||||||
_deploy.update_managed_deploy_config();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Start_name new_construction(Component::Path const &pkg, Verify verify,
|
void _apply_to_construction(Component::Construction_action::Apply_to &fn) override
|
||||||
Component::Info const &info) override
|
|
||||||
{
|
|
||||||
return _runtime_state.new_construction(pkg, verify, info, _affinity_space);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _apply_to_construction(Popup_dialog::Action::Apply_to &fn) override
|
|
||||||
{
|
{
|
||||||
_runtime_state.apply_to_construction([&] (Component &c) { fn.apply_to(c); });
|
_runtime_state.apply_to_construction([&] (Component &c) { fn.apply_to(c); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component::Construction_action interface
|
||||||
|
*/
|
||||||
|
void trigger_pkg_download() override
|
||||||
|
{
|
||||||
|
_runtime_state.apply_to_construction([&] (Component &c) {
|
||||||
|
_download_queue.add(c.path, c.verify); });
|
||||||
|
|
||||||
|
/* incorporate new download-queue content into update */
|
||||||
|
_deploy.update_installation();
|
||||||
|
|
||||||
|
generate_runtime_config();
|
||||||
|
}
|
||||||
|
|
||||||
void discard_construction() override { _runtime_state.discard_construction(); }
|
void discard_construction() override { _runtime_state.discard_construction(); }
|
||||||
|
|
||||||
void launch_construction() override
|
void launch_construction() override
|
||||||
@ -1301,37 +1374,10 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
_deploy.update_managed_deploy_config();
|
_deploy.update_managed_deploy_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void trigger_download(Path const &path, Verify verify) override
|
|
||||||
{
|
|
||||||
_download_queue.remove_inactive_downloads();
|
|
||||||
_download_queue.add(path, verify);
|
|
||||||
|
|
||||||
/* incorporate new download-queue content into update */
|
|
||||||
_deploy.update_installation();
|
|
||||||
|
|
||||||
generate_runtime_config();
|
|
||||||
_generate_dialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_index(Depot::Archive::User const &user) override
|
|
||||||
{
|
|
||||||
auto remove = [&] (Path const &path) {
|
|
||||||
_file_operation_queue.remove_file(path); };
|
|
||||||
|
|
||||||
remove(Path("/rw/depot/", user, "/index/", _sculpt_version));
|
|
||||||
remove(Path("/rw/public/", user, "/index/", _sculpt_version, ".xz"));
|
|
||||||
remove(Path("/rw/public/", user, "/index/", _sculpt_version, ".xz.sig"));
|
|
||||||
|
|
||||||
if (!_file_operation_queue.any_operation_in_progress())
|
|
||||||
_file_operation_queue.schedule_next_operations();
|
|
||||||
|
|
||||||
generate_runtime_config();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Popup_dialog::Construction_info interface
|
* Component::Construction_info interface
|
||||||
*/
|
*/
|
||||||
void _with_construction(Popup_dialog::Construction_info::With const &fn) const override
|
void _with_construction(Component::Construction_info::With const &fn) const override
|
||||||
{
|
{
|
||||||
_runtime_state.with_construction([&] (Component const &c) { fn.with(c); });
|
_runtime_state.with_construction([&] (Component const &c) { fn.with(c); });
|
||||||
}
|
}
|
||||||
@ -1346,21 +1392,18 @@ struct Sculpt::Main : Input_event_handler,
|
|||||||
|
|
||||||
Dialog_view<Diag_dialog> _diag_dialog { _dialog_runtime, *this, _heap };
|
Dialog_view<Diag_dialog> _diag_dialog { _dialog_runtime, *this, _heap };
|
||||||
|
|
||||||
Dialog_view<Popup_dialog> _popup_dialog { _dialog_runtime, _env, *this, *this,
|
Dialog_view<Popup_dialog> _popup_dialog { _dialog_runtime, *this,
|
||||||
|
_build_info, _sculpt_version,
|
||||||
_launchers, _network._nic_state,
|
_launchers, _network._nic_state,
|
||||||
_network._nic_target, _runtime_state,
|
_index_update_queue, _index_rom,
|
||||||
_cached_runtime_config, _download_queue,
|
_download_queue, _runtime_state,
|
||||||
_scan_rom, *this, *this };
|
_cached_runtime_config, _scan_rom,
|
||||||
|
*this };
|
||||||
|
|
||||||
Dialog_view<File_browser_dialog> _file_browser_dialog { _dialog_runtime,
|
Dialog_view<File_browser_dialog> _file_browser_dialog { _dialog_runtime,
|
||||||
_cached_runtime_config,
|
_cached_runtime_config,
|
||||||
_file_browser_state, *this };
|
_file_browser_state, *this };
|
||||||
|
|
||||||
/**
|
|
||||||
* Popup_dialog::Refresh interface
|
|
||||||
*/
|
|
||||||
void refresh_popup_dialog() override { _popup_dialog.refresh(); }
|
|
||||||
|
|
||||||
Managed_config<Main> _fb_drv_config {
|
Managed_config<Main> _fb_drv_config {
|
||||||
_env, "config", "fb_drv", *this, &Main::_handle_fb_drv_config };
|
_env, "config", "fb_drv", *this, &Main::_handle_fb_drv_config };
|
||||||
|
|
||||||
@ -1958,14 +2001,7 @@ void Sculpt::Main::_handle_runtime_state(Xml_node const &state)
|
|||||||
_deploy.update_installation();
|
_deploy.update_installation();
|
||||||
|
|
||||||
/* update depot-user selection after adding new depot URL */
|
/* update depot-user selection after adding new depot URL */
|
||||||
if (_system_visible)
|
if (_system_visible || (_popup.state == Popup::VISIBLE))
|
||||||
trigger_depot_query();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The removal of an index file may have completed, re-query index
|
|
||||||
* files to reflect this change at the depot selection menu.
|
|
||||||
*/
|
|
||||||
if (_popup_dialog.interested_in_file_operations())
|
|
||||||
trigger_depot_query();
|
trigger_depot_query();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define _TYPES_H_
|
#define _TYPES_H_
|
||||||
|
|
||||||
#include <util/list_model.h>
|
#include <util/list_model.h>
|
||||||
|
#include <util/xml_generator.h>
|
||||||
#include <base/env.h>
|
#include <base/env.h>
|
||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <platform_session/platform_session.h>
|
#include <platform_session/platform_session.h>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#ifndef _VIEW__COMPONENT_ADD_WIDGET_H_
|
#ifndef _VIEW__COMPONENT_ADD_WIDGET_H_
|
||||||
#define _VIEW__COMPONENT_ADD_WIDGET_H_
|
#define _VIEW__COMPONENT_ADD_WIDGET_H_
|
||||||
|
|
||||||
|
#include <model/capacity.h>
|
||||||
#include <view/index_menu_widget.h>
|
#include <view/index_menu_widget.h>
|
||||||
#include <view/pd_route_widget.h>
|
#include <view/pd_route_widget.h>
|
||||||
#include <view/resource_widget.h>
|
#include <view/resource_widget.h>
|
||||||
@ -167,6 +168,10 @@ struct Sculpt::Component_add_widget : Widget<Vbox>
|
|||||||
|
|
||||||
_launch.propagate(at);
|
_launch.propagate(at);
|
||||||
|
|
||||||
|
if (at.matching_id<Vbox, Frame, Debug_widget>() == Id { "debug" })
|
||||||
|
action.apply_to_construction([&] (Component &component) {
|
||||||
|
_debug.propagate(at, component); });
|
||||||
|
|
||||||
Id const route_id = at.matching_id<Vbox, Frame, Vbox, Menu_entry>();
|
Id const route_id = at.matching_id<Vbox, Frame, Vbox, Menu_entry>();
|
||||||
|
|
||||||
/* select route to present routing options */
|
/* select route to present routing options */
|
@ -17,6 +17,7 @@
|
|||||||
#include <view/dialog.h>
|
#include <view/dialog.h>
|
||||||
#include <view/text_entry_field.h>
|
#include <view/text_entry_field.h>
|
||||||
#include <model/depot_url.h>
|
#include <model/depot_url.h>
|
||||||
|
#include <xml.h>
|
||||||
|
|
||||||
namespace Sculpt { struct Depot_users_widget; }
|
namespace Sculpt { struct Depot_users_widget; }
|
||||||
|
|
||||||
|
@ -1,532 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Popup dialog
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2018-09-12
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <view/popup_dialog.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace Sculpt;
|
|
||||||
|
|
||||||
|
|
||||||
static Dialog::Id launcher_id(unsigned n) { return { Dialog::Id::Value("launcher ", n) }; }
|
|
||||||
static Dialog::Id user_id (unsigned n) { return { Dialog::Id::Value("user ", n) }; }
|
|
||||||
|
|
||||||
|
|
||||||
static void view_component_info(auto &s, Component const &component)
|
|
||||||
{
|
|
||||||
if (component.info.length() > 1) {
|
|
||||||
s.named_sub_node("label", "info", [&] {
|
|
||||||
s.attribute("text", Component::Info(" ", component.info, " ")); });
|
|
||||||
|
|
||||||
s.template sub_scope<Annotation>("");
|
|
||||||
}
|
|
||||||
s.template sub_scope<Annotation>(component.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Popup_dialog::_view_pkg_elements(Scope<Frame, Vbox> &s,
|
|
||||||
Component const &component) const
|
|
||||||
{
|
|
||||||
using Info = Component::Info;
|
|
||||||
|
|
||||||
s.widget(_back, Index_menu::Name("Add ", Pretty(_construction_name)));
|
|
||||||
|
|
||||||
view_component_info(s, component);
|
|
||||||
|
|
||||||
s.sub_scope<Annotation>(Info(Capacity{component.ram}, " ",
|
|
||||||
component.caps, " caps"));
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
component.routes.for_each([&] (Route const &route) {
|
|
||||||
|
|
||||||
Id const id { Id::Value { count++ } };
|
|
||||||
|
|
||||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
|
||||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Frame, Vbox> &s) {
|
|
||||||
|
|
||||||
bool const selected = _route_selected(id.value);
|
|
||||||
bool const defined = route.selected_service.constructed();
|
|
||||||
|
|
||||||
if (!selected) {
|
|
||||||
Route_entry entry { id };
|
|
||||||
s.widget(entry, defined,
|
|
||||||
defined ? Info(route.selected_service->info)
|
|
||||||
: Info(route));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List of routing options
|
|
||||||
*/
|
|
||||||
if (selected) {
|
|
||||||
Route_entry back { Id { "back" } };
|
|
||||||
s.widget(back, true, Info(route), "back");
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
_runtime_config.for_each_service([&] (Service const &service) {
|
|
||||||
|
|
||||||
Id const service_id { Id::Value("service.", count++) };
|
|
||||||
|
|
||||||
bool const service_selected =
|
|
||||||
route.selected_service.constructed() &&
|
|
||||||
service_id.value == route.selected_service_id;
|
|
||||||
|
|
||||||
if (service.type == route.required) {
|
|
||||||
Service_entry entry { service_id };
|
|
||||||
s.widget(entry, service_selected, service.info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/* don't show the PD menu if only the system PD service is available */
|
|
||||||
if (_runtime_config.num_service_options(Service::Type::PD) > 1)
|
|
||||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
|
||||||
s.widget(_pd_route, _selected_route, component); });
|
|
||||||
|
|
||||||
s.sub_scope<Frame>(Id { "resources" }, [&] (Scope<Frame, Vbox, Frame> &s) {
|
|
||||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Frame, Vbox> &s) {
|
|
||||||
|
|
||||||
bool const selected = _route_selected("resources");
|
|
||||||
|
|
||||||
if (!selected) {
|
|
||||||
Route_entry entry { Id { "resources" } };
|
|
||||||
s.widget(entry, false, "Resource assignment ...", "enter");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected) {
|
|
||||||
Route_entry entry { Id { "back" } };
|
|
||||||
s.widget(entry, true, "Resource assignment ...", "back");
|
|
||||||
|
|
||||||
s.widget(_resources, component);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
s.sub_scope<Frame>([&] (Scope<Frame, Vbox, Frame> &s) {
|
|
||||||
s.widget(_debug, component); });
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Display "Add component" button once all routes are defined
|
|
||||||
*/
|
|
||||||
if (component.all_routes_defined())
|
|
||||||
s.widget(_launch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Popup_dialog::_view_menu_elements(Scope<Frame, Vbox> &s, Xml_node const &depot_users) const
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Lauchers
|
|
||||||
*/
|
|
||||||
if (_state == TOP_LEVEL || _state < DEPOT_SHOWN) {
|
|
||||||
unsigned count = 0;
|
|
||||||
for_each_viewed_launcher([&] (Launchers::Info const &info) {
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { launcher_id(count++) };
|
|
||||||
s.widget(menu_entry, false, String<100>(Pretty(info.path)));
|
|
||||||
});
|
|
||||||
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> depot_menu_entry { Id { "depot" } };
|
|
||||||
s.widget(depot_menu_entry, false, "Depot ...");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Depot users with an available index
|
|
||||||
*/
|
|
||||||
if (_state == DEPOT_SHOWN || _state == INDEX_REQUESTED) {
|
|
||||||
s.widget(_back, "Depot");
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
|
||||||
|
|
||||||
User const name = user.attribute_value("name", User());
|
|
||||||
bool const selected = (_selected_user == name);
|
|
||||||
Id const id = user_id(count++);
|
|
||||||
|
|
||||||
if (_index_avail(name)) {
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { id };
|
|
||||||
s.widget(menu_entry, selected, User(name, " ..."));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Depot selection menu item
|
|
||||||
*/
|
|
||||||
if (_nic_ready()) {
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> menu_entry { Id { "selection" } };
|
|
||||||
s.widget(menu_entry, false, "Selection ...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List of depot users for removing/adding indices
|
|
||||||
*/
|
|
||||||
if (_state == DEPOT_SELECTION) {
|
|
||||||
s.widget(_back, "Selection");
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
|
||||||
|
|
||||||
User const name = user.attribute_value("name", User());
|
|
||||||
bool const selected = _index_avail(name);
|
|
||||||
Id const id = user_id(count++);
|
|
||||||
|
|
||||||
String<32> const suffix = _download_queue.in_progress(_index_path(name))
|
|
||||||
? " fetch... " : " ";
|
|
||||||
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> user_entry { id };
|
|
||||||
s.widget(user_entry, selected, User(name, suffix), "checkbox");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Title of index
|
|
||||||
*/
|
|
||||||
if (_state >= INDEX_SHOWN && _state < PKG_SHOWN) {
|
|
||||||
Index_menu::Name title("Depot ", _selected_user);
|
|
||||||
if (_menu._level)
|
|
||||||
title = Index_menu::Name(title, " ", _menu, " ");
|
|
||||||
|
|
||||||
s.widget(_back, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Index menu
|
|
||||||
*/
|
|
||||||
if (_state >= INDEX_SHOWN && _state < PKG_SHOWN) {
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
_for_each_menu_item([&] (Xml_node item) {
|
|
||||||
|
|
||||||
Id const id { Id::Value(count) };
|
|
||||||
|
|
||||||
if (item.has_type("index")) {
|
|
||||||
auto const name = item.attribute_value("name", Index_menu::Name());
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> entry { id };
|
|
||||||
s.widget(entry, false, Index_menu::Name(name, " ..."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.has_type("pkg")) {
|
|
||||||
auto const path = item.attribute_value("path", Depot::Archive::Path());
|
|
||||||
auto const name = Depot::Archive::name(path);
|
|
||||||
auto const version = Depot::Archive::version(path);
|
|
||||||
|
|
||||||
bool selected = false;
|
|
||||||
|
|
||||||
bool const installing = _download_queue.in_progress(path);
|
|
||||||
|
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
|
||||||
|
|
||||||
if (component.path == path)
|
|
||||||
selected = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
String<100> const text(Pretty(name), " " "(", version, ")",
|
|
||||||
installing ? " installing... " : "... ");
|
|
||||||
|
|
||||||
Hosted<Frame, Vbox, Menu_entry> entry { id };
|
|
||||||
s.widget(entry, selected, text);
|
|
||||||
|
|
||||||
if (selected && !installing) {
|
|
||||||
|
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
|
||||||
|
|
||||||
s.sub_scope<Float>(Id { "install" }, [&] (Scope<Frame, Vbox, Float> &s) {
|
|
||||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox, Float, Vbox> &s) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Package is installed but content is missing
|
|
||||||
*
|
|
||||||
* This can happen when the pkg's runtime is
|
|
||||||
* inconsistent with the content contained in
|
|
||||||
* the pkg's archives.
|
|
||||||
*/
|
|
||||||
if (_blueprint_info.incomplete()) {
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Annotation>(component.path);
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Label>("installed but incomplete");
|
|
||||||
|
|
||||||
if (_nic_ready()) {
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Float>([&] (Scope<Frame, Vbox, Float, Vbox, Float> &s) {
|
|
||||||
s.widget(_install, [&] (Scope<Button> &s) {
|
|
||||||
s.sub_scope<Label>("Reattempt Install"); }); });
|
|
||||||
}
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Package is missing but can be installed
|
|
||||||
*/
|
|
||||||
else if (_blueprint_info.uninstalled() && _nic_ready()) {
|
|
||||||
|
|
||||||
view_component_info(s, component);
|
|
||||||
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Float>([&] (Scope<Frame, Vbox, Float, Vbox, Float> &s) {
|
|
||||||
s.widget(_install, [&] (Scope<Button> &s) {
|
|
||||||
s.sub_scope<Label>("Install"); }); });
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Package is missing and we cannot do anything
|
|
||||||
* about it
|
|
||||||
*/
|
|
||||||
else if (_blueprint_info.uninstalled()) {
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Annotation>(component.path);
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
s.sub_scope<Label>("not installed");
|
|
||||||
s.sub_scope<Vgap>();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Pkg configuration
|
|
||||||
*/
|
|
||||||
if (_state >= PKG_SHOWN)
|
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
|
||||||
_view_pkg_elements(s, component); });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Popup_dialog::click(Clicked_at const &at)
|
|
||||||
{
|
|
||||||
bool clicked_on_back_button = false;
|
|
||||||
|
|
||||||
_back.propagate(at, [&] {
|
|
||||||
clicked_on_back_button = true;
|
|
||||||
|
|
||||||
switch (_state) {
|
|
||||||
case TOP_LEVEL: break;
|
|
||||||
case DEPOT_REQUESTED: break;
|
|
||||||
case DEPOT_SHOWN: _state = TOP_LEVEL; break;
|
|
||||||
case DEPOT_SELECTION: _state = DEPOT_SHOWN; break;
|
|
||||||
case INDEX_REQUESTED: break;
|
|
||||||
|
|
||||||
case INDEX_SHOWN:
|
|
||||||
case PKG_REQUESTED:
|
|
||||||
if (_menu._level > 0) {
|
|
||||||
/* go one menu up */
|
|
||||||
_menu._selected[_menu._level] = Index_menu::Name();
|
|
||||||
_menu._level--;
|
|
||||||
_action.discard_construction();
|
|
||||||
} else {
|
|
||||||
_state = DEPOT_SHOWN;
|
|
||||||
_selected_user = User();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PKG_SHOWN:
|
|
||||||
case ROUTE_SELECTED:
|
|
||||||
_state = INDEX_SHOWN;
|
|
||||||
_action.discard_construction();
|
|
||||||
_selected_route = { };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (clicked_on_back_button)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_launch.propagate(at);
|
|
||||||
_install.propagate(at);
|
|
||||||
|
|
||||||
Id const route_id = at.matching_id<Frame, Vbox, Frame, Vbox, Menu_entry>();
|
|
||||||
|
|
||||||
auto with_matching_user = [&] (Id const &id, auto const &fn)
|
|
||||||
{
|
|
||||||
unsigned count = 0;
|
|
||||||
_depot_users.with_xml([&] (Xml_node const &users) {
|
|
||||||
users.for_each_sub_node("user", [&] (Xml_node const &user) {
|
|
||||||
if (id == user_id(count++))
|
|
||||||
fn(user.attribute_value("name", User())); }); });
|
|
||||||
};
|
|
||||||
|
|
||||||
State const orig_state = _state;
|
|
||||||
|
|
||||||
if (orig_state == TOP_LEVEL) {
|
|
||||||
|
|
||||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
|
||||||
|
|
||||||
if (id.value == "depot") {
|
|
||||||
_state = DEPOT_REQUESTED;
|
|
||||||
_depot_query.trigger_depot_query();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
|
|
||||||
for_each_viewed_launcher([&] (Launchers::Info const &info) {
|
|
||||||
if (id == launcher_id(count++))
|
|
||||||
_action.launch_global(info.path); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (orig_state == DEPOT_SHOWN) {
|
|
||||||
|
|
||||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
|
||||||
|
|
||||||
if (id.value == "selection")
|
|
||||||
_state = DEPOT_SELECTION;
|
|
||||||
|
|
||||||
/* enter depot users menu */
|
|
||||||
with_matching_user(id, [&] (User const &user) {
|
|
||||||
_selected_user = user;
|
|
||||||
_state = INDEX_REQUESTED;
|
|
||||||
_depot_query.trigger_depot_query();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (orig_state == DEPOT_SELECTION) {
|
|
||||||
|
|
||||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
|
||||||
|
|
||||||
with_matching_user(id, [&] (User const &user) {
|
|
||||||
|
|
||||||
if (!_index_avail(user))
|
|
||||||
_action.trigger_download(_index_path(user), Verify{true});
|
|
||||||
else
|
|
||||||
_action.remove_index(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (orig_state >= INDEX_SHOWN && orig_state < PKG_SHOWN) {
|
|
||||||
|
|
||||||
Id const id = at.matching_id<Frame, Vbox, Menu_entry>();
|
|
||||||
|
|
||||||
/* enter sub menu of index */
|
|
||||||
if (_menu._level < Index_menu::MAX_LEVELS - 1) {
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
_for_each_menu_item([&] (Xml_node item) {
|
|
||||||
|
|
||||||
if (id.value == Id::Value(count)) {
|
|
||||||
|
|
||||||
if (item.has_type("index")) {
|
|
||||||
|
|
||||||
Index_menu::Name const name =
|
|
||||||
item.attribute_value("name", Index_menu::Name());
|
|
||||||
|
|
||||||
_menu._selected[_menu._level] = name;
|
|
||||||
_menu._level++;
|
|
||||||
|
|
||||||
} else if (item.has_type("pkg")) {
|
|
||||||
|
|
||||||
auto path = item.attribute_value("path", Component::Path());
|
|
||||||
auto info = item.attribute_value("info", Component::Info());
|
|
||||||
|
|
||||||
_construction_name =
|
|
||||||
_action.new_construction(path, Verify{true}, info);
|
|
||||||
|
|
||||||
_state = PKG_REQUESTED;
|
|
||||||
_depot_query.trigger_depot_query();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (orig_state == PKG_SHOWN) {
|
|
||||||
|
|
||||||
/* select route to present routing options */
|
|
||||||
if (route_id.valid()) {
|
|
||||||
_state = ROUTE_SELECTED;
|
|
||||||
_selected_route = route_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (orig_state == ROUTE_SELECTED) {
|
|
||||||
|
|
||||||
/* close selected route */
|
|
||||||
if (route_id.value == "back") {
|
|
||||||
_state = PKG_SHOWN;
|
|
||||||
_selected_route = { };
|
|
||||||
|
|
||||||
} else if (_resource_dialog_selected()) {
|
|
||||||
|
|
||||||
bool const clicked_on_different_route = route_id.valid();
|
|
||||||
if (clicked_on_different_route) {
|
|
||||||
_selected_route = route_id;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
_action.apply_to_construction([&] (Component &component) {
|
|
||||||
_resources.propagate(at, component); });
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
bool clicked_on_selected_route = false;
|
|
||||||
|
|
||||||
_apply_to_selected_route(_action, [&] (Route &route) {
|
|
||||||
|
|
||||||
unsigned count = 0;
|
|
||||||
_runtime_config.for_each_service([&] (Service const &service) {
|
|
||||||
|
|
||||||
Id const id { Id::Value("service.", count++) };
|
|
||||||
|
|
||||||
if (route_id == id) {
|
|
||||||
|
|
||||||
bool const clicked_service_already_selected =
|
|
||||||
route.selected_service.constructed() &&
|
|
||||||
id.value == route.selected_service_id;
|
|
||||||
|
|
||||||
if (clicked_service_already_selected) {
|
|
||||||
|
|
||||||
/* clear selection */
|
|
||||||
route.selected_service.destruct();
|
|
||||||
route.selected_service_id = { };
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* select different service */
|
|
||||||
route.selected_service.construct(service);
|
|
||||||
route.selected_service_id = id.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_state = PKG_SHOWN;
|
|
||||||
_selected_route = { };
|
|
||||||
|
|
||||||
clicked_on_selected_route = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_selected_route == _pd_route.id)
|
|
||||||
_action.apply_to_construction([&] (Component &component) {
|
|
||||||
_pd_route.propagate(at, component); });
|
|
||||||
|
|
||||||
/* select different route */
|
|
||||||
if (!clicked_on_selected_route && route_id.valid()) {
|
|
||||||
_state = ROUTE_SELECTED;
|
|
||||||
_selected_route = route_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orig_state == PKG_SHOWN || orig_state == ROUTE_SELECTED)
|
|
||||||
_action.apply_to_construction([&] (Component &component) {
|
|
||||||
_debug.propagate(at, component); });
|
|
||||||
}
|
|
@ -17,330 +17,88 @@
|
|||||||
/* local includes */
|
/* local includes */
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
#include <model/launchers.h>
|
#include <model/launchers.h>
|
||||||
#include <model/sculpt_version.h>
|
#include <view/popup_tabs_widget.h>
|
||||||
#include <model/component.h>
|
#include <view/popup_options_widget.h>
|
||||||
#include <model/runtime_config.h>
|
#include <view/software_add_widget.h>
|
||||||
#include <model/download_queue.h>
|
|
||||||
#include <model/nic_state.h>
|
|
||||||
#include <model/index_menu.h>
|
|
||||||
#include <view/dialog.h>
|
|
||||||
#include <view/pd_route_widget.h>
|
|
||||||
#include <view/resource_widget.h>
|
|
||||||
#include <view/debug_widget.h>
|
|
||||||
#include <depot_query.h>
|
|
||||||
|
|
||||||
namespace Sculpt { struct Popup_dialog; }
|
namespace Sculpt { struct Popup_dialog; }
|
||||||
|
|
||||||
|
|
||||||
struct Sculpt::Popup_dialog : Dialog::Top_level_dialog
|
struct Sculpt::Popup_dialog : Dialog::Top_level_dialog
|
||||||
{
|
{
|
||||||
using Depot_users = Rom_data;
|
using Depot_users = Rom_data;
|
||||||
using Blueprint_info = Component::Blueprint_info;
|
using Construction_info = Component::Construction_info;
|
||||||
|
using Index = Software_add_widget::Index;
|
||||||
|
|
||||||
Env &_env;
|
struct Action : virtual Software_add_widget::Action,
|
||||||
|
virtual Popup_options_widget::Action { };
|
||||||
Sculpt_version const _sculpt_version { _env };
|
|
||||||
|
|
||||||
Launchers const &_launchers;
|
|
||||||
Nic_state const &_nic_state;
|
|
||||||
Nic_target const &_nic_target;
|
|
||||||
|
|
||||||
bool _nic_ready() const { return _nic_target.ready() && _nic_state.ready(); }
|
|
||||||
|
|
||||||
Runtime_info const &_runtime_info;
|
|
||||||
Runtime_config const &_runtime_config;
|
|
||||||
Download_queue const &_download_queue;
|
|
||||||
Depot_users const &_depot_users;
|
|
||||||
|
|
||||||
Depot_query &_depot_query;
|
|
||||||
|
|
||||||
struct Construction_info : Interface
|
|
||||||
{
|
|
||||||
struct With : Interface { virtual void with(Component const &) const = 0; };
|
|
||||||
|
|
||||||
virtual void _with_construction(With const &) const = 0;
|
|
||||||
|
|
||||||
template <typename FN>
|
|
||||||
void with_construction(FN const &fn) const
|
|
||||||
{
|
|
||||||
struct _With : With {
|
|
||||||
FN const &_fn;
|
|
||||||
_With(FN const &fn) : _fn(fn) { }
|
|
||||||
void with(Component const &c) const override { _fn(c); }
|
|
||||||
};
|
|
||||||
_with_construction(_With(fn));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Refresh : Interface, Noncopyable
|
|
||||||
{
|
|
||||||
virtual void refresh_popup_dialog() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Refresh &_refresh;
|
|
||||||
|
|
||||||
struct Action : Interface
|
|
||||||
{
|
|
||||||
virtual void launch_global(Path const &launcher) = 0;
|
|
||||||
|
|
||||||
virtual Start_name new_construction(Component::Path const &pkg, Verify,
|
|
||||||
Component::Info const &info) = 0;
|
|
||||||
|
|
||||||
struct Apply_to : Interface { virtual void apply_to(Component &) = 0; };
|
|
||||||
|
|
||||||
virtual void _apply_to_construction(Apply_to &) = 0;
|
|
||||||
|
|
||||||
template <typename FN>
|
|
||||||
void apply_to_construction(FN const &fn)
|
|
||||||
{
|
|
||||||
struct _Apply_to : Apply_to {
|
|
||||||
FN const &_fn;
|
|
||||||
_Apply_to(FN const &fn) : _fn(fn) { }
|
|
||||||
void apply_to(Component &c) override { _fn(c); }
|
|
||||||
} apply_fn(fn);
|
|
||||||
|
|
||||||
_apply_to_construction(apply_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void discard_construction() = 0;
|
|
||||||
virtual void launch_construction() = 0;
|
|
||||||
|
|
||||||
virtual void trigger_download(Path const &, Verify) = 0;
|
|
||||||
virtual void remove_index(Depot::Archive::User const &) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Action &_action;
|
Action &_action;
|
||||||
|
|
||||||
Construction_info const &_construction_info;
|
Hosted<Frame, Vbox, Popup_tabs_widget> _tabs { Id { "tabs" } };
|
||||||
|
Hosted<Frame, Vbox, Software_add_widget> _add;
|
||||||
using Route_entry = Hosted<Frame, Vbox, Frame, Vbox, Menu_entry>;
|
Hosted<Frame, Vbox, Popup_options_widget> _options;
|
||||||
using Service_entry = Hosted<Frame, Vbox, Frame, Vbox, Menu_entry>;
|
|
||||||
|
|
||||||
struct Sub_menu_title : Widget<Left_floating_hbox>
|
|
||||||
{
|
|
||||||
void view(Scope<Left_floating_hbox> &s, auto const &text) const
|
|
||||||
{
|
|
||||||
bool const hovered = (s.hovered() && !s.dragged());
|
|
||||||
|
|
||||||
s.sub_scope<Icon>("back", Icon::Attr { .hovered = hovered,
|
|
||||||
.selected = true });
|
|
||||||
s.sub_scope<Label>(" ");
|
|
||||||
s.sub_scope<Label>(text, [&] (auto &s) {
|
|
||||||
s.attribute("font", "title/regular"); });
|
|
||||||
|
|
||||||
/* inflate vertical space to button size */
|
|
||||||
s.sub_scope<Button>([&] (Scope<Left_floating_hbox, Button> &s) {
|
|
||||||
s.attribute("style", "invisible");
|
|
||||||
s.sub_scope<Label>(""); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void click(Clicked_at const &, auto const &fn) { fn(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
Hosted<Frame, Vbox, Sub_menu_title> _back { Id { "back" } };
|
|
||||||
Hosted<Frame, Vbox, Deferred_action_button> _launch { Id { "Add component" } };
|
|
||||||
Hosted<Frame, Vbox, Float, Vbox, Float, Deferred_action_button> _install { Id { "install" } };
|
|
||||||
Hosted<Frame, Vbox, Frame, Vbox, Resource_widget> _resources { Id { "resources" } };
|
|
||||||
Hosted<Frame, Vbox, Frame, Pd_route_widget> _pd_route { Id { "pd_route" }, _runtime_config };
|
|
||||||
Hosted<Frame, Vbox, Frame, Debug_widget> _debug { Id { "debug" } };
|
|
||||||
|
|
||||||
enum State { TOP_LEVEL, DEPOT_REQUESTED, DEPOT_SHOWN, DEPOT_SELECTION,
|
|
||||||
INDEX_REQUESTED, INDEX_SHOWN,
|
|
||||||
PKG_REQUESTED, PKG_SHOWN, ROUTE_SELECTED };
|
|
||||||
|
|
||||||
State _state { TOP_LEVEL };
|
|
||||||
|
|
||||||
typedef Depot::Archive::User User;
|
|
||||||
User _selected_user { };
|
|
||||||
|
|
||||||
Blueprint_info _blueprint_info { };
|
|
||||||
|
|
||||||
Component::Name _construction_name { };
|
|
||||||
|
|
||||||
Id _selected_route { };
|
|
||||||
|
|
||||||
bool _route_selected(Route::Id const &id) const
|
|
||||||
{
|
|
||||||
return _selected_route.valid() && id == _selected_route.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _resource_dialog_selected() const
|
|
||||||
{
|
|
||||||
return _route_selected("resources");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _apply_to_selected_route(Action &action, auto const &fn)
|
|
||||||
{
|
|
||||||
unsigned cnt = 0;
|
|
||||||
action.apply_to_construction([&] (Component &component) {
|
|
||||||
component.routes.for_each([&] (Route &route) {
|
|
||||||
if (_route_selected(Route::Id(cnt++)))
|
|
||||||
fn(route); }); });
|
|
||||||
}
|
|
||||||
|
|
||||||
Index_menu _menu { };
|
|
||||||
|
|
||||||
void depot_users_scan_updated()
|
|
||||||
{
|
|
||||||
if (_state == DEPOT_REQUESTED)
|
|
||||||
_state = DEPOT_SHOWN;
|
|
||||||
|
|
||||||
if (_state != TOP_LEVEL)
|
|
||||||
_refresh.refresh_popup_dialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
Attached_rom_dataspace _index_rom { _env, "report -> runtime/depot_query/index" };
|
|
||||||
|
|
||||||
Signal_handler<Popup_dialog> _index_handler {
|
|
||||||
_env.ep(), *this, &Popup_dialog::_handle_index };
|
|
||||||
|
|
||||||
void _handle_index()
|
|
||||||
{
|
|
||||||
/* prevent modifications of index while browsing it */
|
|
||||||
if (_state >= INDEX_SHOWN)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_index_rom.update();
|
|
||||||
|
|
||||||
if (_state == INDEX_REQUESTED)
|
|
||||||
_state = INDEX_SHOWN;
|
|
||||||
|
|
||||||
_refresh.refresh_popup_dialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _index_avail(User const &user) const
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
_index_rom.xml().for_each_sub_node("index", [&] (Xml_node index) {
|
|
||||||
if (index.attribute_value("user", User()) == user)
|
|
||||||
result = true; });
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
Path _index_path(User const &user) const
|
|
||||||
{
|
|
||||||
return Path(user, "/index/", _sculpt_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _for_each_menu_item(auto const &fn) const
|
|
||||||
{
|
|
||||||
_menu.for_each_item(_index_rom.xml(), _selected_user, fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _view_pkg_elements (Scope<Frame, Vbox> &, Component const &) const;
|
|
||||||
void _view_menu_elements(Scope<Frame, Vbox> &, Xml_node const &depot_users) const;
|
|
||||||
|
|
||||||
void view(Scope<> &s) const override
|
void view(Scope<> &s) const override
|
||||||
{
|
{
|
||||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
||||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||||
_depot_users.with_xml([&] (Xml_node const &users) {
|
s.widget(_tabs);
|
||||||
_view_menu_elements(s, users); }); }); });
|
if (_tabs.add_selected()) {
|
||||||
|
using Attr = Software_add_widget::Attr;
|
||||||
|
s.widget(_add, Attr { .visible_frames = false,
|
||||||
|
.left_aligned_items = true });
|
||||||
|
}
|
||||||
|
if (_tabs.options_selected())
|
||||||
|
s.widget(_options);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void click(Clicked_at const &) override;
|
void click(Clicked_at const &at) override
|
||||||
|
{
|
||||||
|
_tabs.propagate(at, [&] { });
|
||||||
|
_add.propagate(at, _action);
|
||||||
|
_options.propagate(at, _action);
|
||||||
|
}
|
||||||
|
|
||||||
void clack(Clacked_at const &at) override
|
void clack(Clacked_at const &at) override
|
||||||
{
|
{
|
||||||
_launch.propagate(at, [&] {
|
_add.propagate(at, _action);
|
||||||
_action.launch_construction();
|
|
||||||
reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
_install.propagate(at, [&] {
|
|
||||||
bool const pkg_need_install = !_blueprint_info.pkg_avail
|
|
||||||
|| _blueprint_info.incomplete();
|
|
||||||
if (pkg_need_install) {
|
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
|
||||||
_action.trigger_download(component.path, component.verify);
|
|
||||||
_refresh.refresh_popup_dialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drag(Dragged_at const &) override { }
|
void handle_key(Codepoint c, Action &action)
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
{
|
||||||
_state = TOP_LEVEL;
|
if (_tabs.add_selected())
|
||||||
_selected_user = User();
|
_add.handle_key(c, action);
|
||||||
_selected_route = { };
|
|
||||||
_menu._level = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Popup_dialog(Env &env, Refresh &refresh, Action &action,
|
Popup_dialog(Action &action,
|
||||||
Launchers const &launchers,
|
Build_info const &build_info,
|
||||||
Nic_state const &nic_state,
|
Sculpt_version const &sculpt_version,
|
||||||
Nic_target const &nic_target,
|
Launchers const &launchers,
|
||||||
Runtime_info const &runtime_info,
|
Nic_state const &nic_state,
|
||||||
Runtime_config const &runtime_config,
|
Index_update_queue const &index_update_queue,
|
||||||
Download_queue const &download_queue,
|
Index const &index,
|
||||||
Depot_users const &depot_users,
|
Download_queue const &download_queue,
|
||||||
Depot_query &depot_query,
|
Runtime_info const &runtime_info,
|
||||||
Construction_info const &construction_info)
|
Runtime_config const &runtime_config,
|
||||||
|
Depot_users const &depot_users,
|
||||||
|
Construction_info const &construction_info)
|
||||||
:
|
:
|
||||||
Top_level_dialog("popup"),
|
Top_level_dialog("popup"), _action(action),
|
||||||
_env(env), _launchers(launchers),
|
_add(Id { "add" }, build_info, sculpt_version, nic_state,
|
||||||
_nic_state(nic_state), _nic_target(nic_target),
|
index_update_queue, index, download_queue, runtime_config,
|
||||||
_runtime_info(runtime_info), _runtime_config(runtime_config),
|
construction_info, depot_users),
|
||||||
_download_queue(download_queue), _depot_users(depot_users),
|
_options(Id { "options" }, runtime_info, launchers)
|
||||||
_depot_query(depot_query), _refresh(refresh), _action(action),
|
{ }
|
||||||
_construction_info(construction_info)
|
|
||||||
{
|
|
||||||
_index_rom.sigh(_index_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool depot_query_needs_users() const { return _state >= TOP_LEVEL; }
|
bool watches_depot() const { return _tabs.add_selected(); }
|
||||||
|
|
||||||
void gen_depot_query(Xml_generator &xml, Xml_node const &depot_users) const
|
bool keyboard_needed() const { return _tabs.add_selected()
|
||||||
{
|
&& _add.keyboard_needed(); }
|
||||||
if (_state >= TOP_LEVEL)
|
|
||||||
depot_users.for_each_sub_node("user", [&] (Xml_node user) {
|
|
||||||
xml.node("index", [&] {
|
|
||||||
User const name = user.attribute_value("name", User());
|
|
||||||
xml.attribute("user", name);
|
|
||||||
xml.attribute("version", _sculpt_version);
|
|
||||||
if (_state >= INDEX_REQUESTED && _selected_user == name)
|
|
||||||
xml.attribute("content", "yes");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_state >= PKG_REQUESTED)
|
void sanitize_user_selection() { _add.sanitize_user_selection(); }
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
|
||||||
xml.node("blueprint", [&] {
|
|
||||||
xml.attribute("pkg", component.path); }); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void for_each_viewed_launcher(auto const &fn) const
|
|
||||||
{
|
|
||||||
_launchers.for_each([&] (Launchers::Info const &info) {
|
|
||||||
if (_runtime_info.present_in_runtime(info.path))
|
|
||||||
return;
|
|
||||||
|
|
||||||
fn(info);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_blueprint(Component &construction, Xml_node blueprint)
|
|
||||||
{
|
|
||||||
if (_state < PKG_REQUESTED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
construction.try_apply_blueprint(blueprint);
|
|
||||||
|
|
||||||
_blueprint_info = construction.blueprint_info;
|
|
||||||
|
|
||||||
if (_blueprint_info.ready_to_deploy() && _state == PKG_REQUESTED)
|
|
||||||
_state = PKG_SHOWN;
|
|
||||||
|
|
||||||
_refresh.refresh_popup_dialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool interested_in_file_operations() const
|
|
||||||
{
|
|
||||||
return _state == DEPOT_SELECTION;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _VIEW__POPUP_DIALOG_H_ */
|
#endif /* _VIEW__POPUP_DIALOG_H_ */
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* \brief Widget for the software options
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2024-04-02
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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__POPUP_OPTIONS_WIDGET_H_
|
||||||
|
#define _VIEW__POPUP_OPTIONS_WIDGET_H_
|
||||||
|
|
||||||
|
#include <model/launchers.h>
|
||||||
|
#include <view/dialog.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace Sculpt { struct Popup_options_widget; }
|
||||||
|
|
||||||
|
|
||||||
|
struct Sculpt::Popup_options_widget : Widget<Vbox>
|
||||||
|
{
|
||||||
|
Runtime_info const &_runtime_info;
|
||||||
|
Launchers const &_launchers;
|
||||||
|
|
||||||
|
Popup_options_widget(Runtime_info const &runtime_info,
|
||||||
|
Launchers const &launchers)
|
||||||
|
:
|
||||||
|
_runtime_info(runtime_info), _launchers(launchers)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
struct Option : Menu_entry
|
||||||
|
{
|
||||||
|
void view(Scope<Left_floating_hbox> &s, auto const &text, bool enabled) const
|
||||||
|
{
|
||||||
|
Menu_entry::view(s, enabled, Pretty(text), "checkbox");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Hosted_option = Hosted<Vbox, Option>;
|
||||||
|
|
||||||
|
void view(Scope<Vbox> &s) const
|
||||||
|
{
|
||||||
|
unsigned count = 0;
|
||||||
|
_launchers.for_each([&] (Launchers::Info const &info) {
|
||||||
|
Hosted_option option { { count++ } };
|
||||||
|
s.widget(option, info.path, _runtime_info.present_in_runtime(info.path));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Action : Interface
|
||||||
|
{
|
||||||
|
virtual void enable_optional_component (Path const &launcher) = 0;
|
||||||
|
virtual void disable_optional_component(Path const &launcher) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void click(Clicked_at const &at, Action &action) const
|
||||||
|
{
|
||||||
|
Id const clicked_id = at.matching_id<Vbox, Option>();
|
||||||
|
|
||||||
|
unsigned count = 0;
|
||||||
|
_launchers.for_each([&] (Launchers::Info const &info) {
|
||||||
|
Id const id { { count++ } };
|
||||||
|
if (clicked_id != id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Hosted_option const option { id };
|
||||||
|
option.propagate(at, [&] {
|
||||||
|
if (_runtime_info.present_in_runtime(info.path))
|
||||||
|
action.disable_optional_component(info.path);
|
||||||
|
else
|
||||||
|
action.enable_optional_component(info.path);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _VIEW__POPUP_OPTIONS_WIDGET_H_ */
|
50
repos/gems/src/app/sculpt_manager/view/popup_tabs_widget.h
Normal file
50
repos/gems/src/app/sculpt_manager/view/popup_tabs_widget.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* \brief Widget for the tabs displayed in the popup dialog
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2024-03-28
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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__POPUP_TABS_WIDGET_H_
|
||||||
|
#define _VIEW__POPUP_TABS_WIDGET_H_
|
||||||
|
|
||||||
|
#include <view/dialog.h>
|
||||||
|
|
||||||
|
namespace Sculpt { struct Popup_tabs_widget; }
|
||||||
|
|
||||||
|
|
||||||
|
struct Sculpt::Popup_tabs_widget : Widget<Hbox>
|
||||||
|
{
|
||||||
|
enum class Tab { ADD, OPTIONS };
|
||||||
|
|
||||||
|
Tab _selected = Tab::OPTIONS;
|
||||||
|
|
||||||
|
Hosted<Hbox, Select_button<Tab>> const
|
||||||
|
_add { Id { "Add" }, Tab::ADD },
|
||||||
|
_options { Id { "Options" }, Tab::OPTIONS };
|
||||||
|
|
||||||
|
void view(Scope<Hbox> &s) const
|
||||||
|
{
|
||||||
|
s.widget(_add, _selected);
|
||||||
|
s.widget(_options, _selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void click(Clicked_at const &at, auto const &fn)
|
||||||
|
{
|
||||||
|
_add .propagate(at, [&] (Tab t) { _selected = t; });
|
||||||
|
_options.propagate(at, [&] (Tab t) { _selected = t; });
|
||||||
|
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool options_selected() const { return _selected == Tab::OPTIONS; }
|
||||||
|
bool add_selected() const { return _selected == Tab::ADD; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _VIEW__POPUP_TABS_WIDGET_H_ */
|
@ -18,6 +18,7 @@
|
|||||||
#include <model/nic_state.h>
|
#include <model/nic_state.h>
|
||||||
#include <model/index_update_queue.h>
|
#include <model/index_update_queue.h>
|
||||||
#include <model/index_menu.h>
|
#include <model/index_menu.h>
|
||||||
|
#include <model/sculpt_version.h>
|
||||||
#include <view/depot_users_widget.h>
|
#include <view/depot_users_widget.h>
|
||||||
#include <view/index_menu_widget.h>
|
#include <view/index_menu_widget.h>
|
||||||
#include <view/index_pkg_widget.h>
|
#include <view/index_pkg_widget.h>
|
||||||
@ -109,7 +110,7 @@ struct Sculpt::Software_add_widget : Widget_interface<Vbox>
|
|||||||
if (component.path == pkg_path)
|
if (component.path == pkg_path)
|
||||||
pkg_selected = true; });
|
pkg_selected = true; });
|
||||||
|
|
||||||
label = { Pretty(label), "(", Depot::Archive::version(pkg_path), ")",
|
label = { Pretty(label), " (", Depot::Archive::version(pkg_path), ")",
|
||||||
pkg_installing ? " installing... " : "... " };
|
pkg_installing ? " installing... " : "... " };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,9 +135,14 @@ struct Sculpt::Software_add_widget : Widget_interface<Vbox>
|
|||||||
|
|
||||||
Hosted<Vbox, Frame, Vbox, Float, Operation_button> _check { Id { "check" } };
|
Hosted<Vbox, Frame, Vbox, Float, Operation_button> _check { Id { "check" } };
|
||||||
|
|
||||||
void view(Scope<Vbox> &s) const
|
struct Attr { bool visible_frames, left_aligned_items; };
|
||||||
|
|
||||||
|
void view(Scope<Vbox> &s, Attr const attr) const
|
||||||
{
|
{
|
||||||
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
s.sub_scope<Frame>([&] (Scope<Vbox, Frame> &s) {
|
||||||
|
if (!attr.visible_frames)
|
||||||
|
s.attribute("style", "invisible");
|
||||||
|
|
||||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||||
s.widget(_users);
|
s.widget(_users);
|
||||||
|
|
||||||
@ -163,18 +169,25 @@ struct Sculpt::Software_add_widget : Widget_interface<Vbox>
|
|||||||
if (_users.unfolded())
|
if (_users.unfolded())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s.sub_scope<Vgap>();
|
if (attr.visible_frames)
|
||||||
|
s.sub_scope<Vgap>();
|
||||||
|
|
||||||
User const user = _users.selected();
|
User const user = _users.selected();
|
||||||
if (!_component_add_widget_visible() && !_menu.anything_visible(user))
|
if (!_component_add_widget_visible() && !_menu.anything_visible(user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool const resource_dialog = _component_add_widget_visible();
|
||||||
|
|
||||||
s.sub_scope<Float>([&] (Scope<Vbox, Float> &s) {
|
s.sub_scope<Float>([&] (Scope<Vbox, Float> &s) {
|
||||||
|
if (attr.left_aligned_items && !resource_dialog)
|
||||||
|
s.attribute("west", "yes");
|
||||||
s.sub_scope<Frame>([&] (Scope<Vbox, Float, Frame> &s) {
|
s.sub_scope<Frame>([&] (Scope<Vbox, Float, Frame> &s) {
|
||||||
|
if (!attr.visible_frames)
|
||||||
|
s.attribute("style", "invisible");
|
||||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Float, Frame, Vbox> &s) {
|
s.sub_scope<Vbox>([&] (Scope<Vbox, Float, Frame, Vbox> &s) {
|
||||||
s.sub_scope<Min_ex>(35);
|
s.sub_scope<Min_ex>(35);
|
||||||
|
|
||||||
if (_component_add_widget_visible())
|
if (resource_dialog)
|
||||||
_construction_info.with_construction([&] (Component const &component) {
|
_construction_info.with_construction([&] (Component const &component) {
|
||||||
s.widget(_component_add, component); });
|
s.widget(_component_add, component); });
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user