mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 10:46:25 +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()
|
||||
&& _storage._selected_target.valid());
|
||||
|
||||
s.widget(_software_add_widget, _software_title_bar.selected()
|
||||
&& _software_tabs_widget.hosted.add_selected()
|
||||
&& _storage._selected_target.valid());
|
||||
{
|
||||
using Attr = Software_add_widget::Attr;
|
||||
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) {
|
||||
s.widget(_software_update_widget, _software_title_bar.selected()
|
||||
|
@ -18,17 +18,19 @@
|
||||
#include <os/reporter.h>
|
||||
|
||||
/* local includes */
|
||||
#include <model/wpa_passphrase.h>
|
||||
#include <types.h>
|
||||
#include <model/popup.h>
|
||||
#include <model/wpa_passphrase.h>
|
||||
#include <view/network_widget.h>
|
||||
#include <view/panel_dialog.h>
|
||||
#include <view/system_dialog.h>
|
||||
#include <view/popup_dialog.h>
|
||||
|
||||
namespace Sculpt { struct 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;
|
||||
|
||||
@ -36,13 +38,21 @@ struct Sculpt::Keyboard_focus
|
||||
Wpa_passphrase &_wpa_passphrase;
|
||||
Panel_dialog::State const &_panel;
|
||||
System_dialog const &_system_dialog;
|
||||
Popup_dialog const &_popup_dialog;
|
||||
bool const &_system_visible;
|
||||
Popup const &_popup;
|
||||
|
||||
void update()
|
||||
{
|
||||
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())
|
||||
target = WPA_PASSPHRASE;
|
||||
@ -61,10 +71,13 @@ struct Sculpt::Keyboard_focus
|
||||
|
||||
case WPA_PASSPHRASE:
|
||||
case SYSTEM_DIALOG:
|
||||
case POPUP:
|
||||
xml.attribute("label", "manager -> input");
|
||||
break;
|
||||
|
||||
case INITIAL:
|
||||
case UNDEFINED:
|
||||
break;
|
||||
|
||||
case WM:
|
||||
xml.attribute("label", "wm -> ");
|
||||
break;
|
||||
@ -77,14 +90,18 @@ struct Sculpt::Keyboard_focus
|
||||
Wpa_passphrase &wpa_passphrase,
|
||||
Panel_dialog::State const &panel,
|
||||
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"),
|
||||
_network_widget(network_widget),
|
||||
_wpa_passphrase(wpa_passphrase),
|
||||
_panel(panel),
|
||||
_system_dialog(system_dialog),
|
||||
_system_visible(system_visible)
|
||||
_popup_dialog(popup_dialog),
|
||||
_system_visible(system_visible),
|
||||
_popup(popup)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
@ -64,16 +64,15 @@ struct Sculpt::Main : Input_event_handler,
|
||||
Network::Info,
|
||||
Graph::Action,
|
||||
Panel_dialog::Action,
|
||||
Popup_dialog::Action,
|
||||
Network_widget::Action,
|
||||
Settings_widget::Action,
|
||||
Software_presets_widget::Action,
|
||||
Software_update_widget::Action,
|
||||
File_browser_dialog::Action,
|
||||
Popup_dialog::Construction_info,
|
||||
Popup_dialog::Action,
|
||||
Component::Construction_info,
|
||||
Depot_query,
|
||||
Panel_dialog::State,
|
||||
Popup_dialog::Refresh,
|
||||
Screensaver::Action,
|
||||
Drivers::Info,
|
||||
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 _index_user = _build_info.depot_user;
|
||||
|
||||
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("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.attribute("users", "yes"); });
|
||||
|
||||
if (_system_dialog_watches_depot() || !_scan_rom.valid())
|
||||
xml.node("scan", [&] {
|
||||
xml.attribute("users", "yes"); });
|
||||
if (_popup_dialog.watches_depot() || !_image_index_rom.valid())
|
||||
xml.node("index", [&] {
|
||||
xml.attribute("user", _index_user);
|
||||
xml.attribute("version", _sculpt_version);
|
||||
xml.attribute("content", "yes");
|
||||
});
|
||||
|
||||
if (_system_dialog_watches_depot() || !_image_index_rom.valid())
|
||||
xml.node("image_index", [&] {
|
||||
@ -457,8 +464,9 @@ struct Sculpt::Main : Input_event_handler,
|
||||
xml.attribute("user", _image_index_user);
|
||||
});
|
||||
|
||||
_scan_rom.with_xml([&] (Xml_node const &scan) {
|
||||
_popup_dialog.gen_depot_query(xml, scan); });
|
||||
_runtime_state.with_construction([&] (Component const &component) {
|
||||
xml.node("blueprint", [&] {
|
||||
xml.attribute("pkg", component.path); }); });
|
||||
|
||||
/* update query for blueprints of all unconfigured start nodes */
|
||||
_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 **
|
||||
*********************/
|
||||
@ -502,9 +533,10 @@ struct Sculpt::Main : Input_event_handler,
|
||||
return;
|
||||
|
||||
_runtime_state.apply_to_construction([&] (Component &component) {
|
||||
_popup_dialog.apply_blueprint(component, blueprint); });
|
||||
component.try_apply_blueprint(blueprint); });
|
||||
|
||||
_deploy.handle_deploy();
|
||||
_popup_dialog.refresh();
|
||||
}
|
||||
|
||||
|
||||
@ -519,8 +551,9 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
void _handle_scan(Xml_node const &)
|
||||
{
|
||||
_popup_dialog.depot_users_scan_updated();
|
||||
_system_dialog.sanitize_user_selection();
|
||||
_popup_dialog.sanitize_user_selection();
|
||||
_popup_dialog.refresh();
|
||||
}
|
||||
|
||||
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,
|
||||
*this, _system_dialog, _system_visible };
|
||||
*this, _system_dialog, _system_visible,
|
||||
_popup_dialog, _popup };
|
||||
|
||||
struct Keyboard_focus_guard
|
||||
{
|
||||
@ -786,14 +820,18 @@ struct Sculpt::Main : Input_event_handler,
|
||||
ev.handle_press([&] (Input::Keycode, Codepoint code) {
|
||||
if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|
||||
_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())
|
||||
_system_dialog.handle_key(code, *this);
|
||||
|
||||
need_generate_dialog = true;
|
||||
});
|
||||
|
||||
if (need_generate_dialog)
|
||||
if (need_generate_dialog) {
|
||||
_generate_dialog();
|
||||
_popup_dialog.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -996,6 +1034,41 @@ struct Sculpt::Main : Input_event_handler,
|
||||
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
|
||||
*/
|
||||
@ -1254,7 +1327,6 @@ struct Sculpt::Main : Input_event_handler,
|
||||
{
|
||||
/* close popup menu */
|
||||
_popup.state = Popup::OFF;
|
||||
_popup_dialog.reset();
|
||||
_popup_dialog.refresh();
|
||||
|
||||
/* remove popup window from window layout */
|
||||
@ -1264,31 +1336,32 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_graph_view.refresh();
|
||||
}
|
||||
|
||||
/*
|
||||
* Popup_dialog::Action interface
|
||||
*/
|
||||
void launch_global(Path const &launcher) override
|
||||
void new_construction(Component::Path const &pkg, Verify verify,
|
||||
Component::Info const &info) override
|
||||
{
|
||||
_runtime_state.launch(launcher, launcher);
|
||||
|
||||
_close_popup_dialog();
|
||||
|
||||
/* trigger change of the deployment */
|
||||
_download_queue.remove_inactive_downloads();
|
||||
_deploy.update_managed_deploy_config();
|
||||
_runtime_state.new_construction(pkg, verify, info, _affinity_space);
|
||||
trigger_depot_query();
|
||||
}
|
||||
|
||||
Start_name new_construction(Component::Path const &pkg, Verify verify,
|
||||
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
|
||||
void _apply_to_construction(Component::Construction_action::Apply_to &fn) override
|
||||
{
|
||||
_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 launch_construction() override
|
||||
@ -1301,37 +1374,10 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_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); });
|
||||
}
|
||||
@ -1346,21 +1392,18 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
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,
|
||||
_network._nic_target, _runtime_state,
|
||||
_cached_runtime_config, _download_queue,
|
||||
_scan_rom, *this, *this };
|
||||
_index_update_queue, _index_rom,
|
||||
_download_queue, _runtime_state,
|
||||
_cached_runtime_config, _scan_rom,
|
||||
*this };
|
||||
|
||||
Dialog_view<File_browser_dialog> _file_browser_dialog { _dialog_runtime,
|
||||
_cached_runtime_config,
|
||||
_file_browser_state, *this };
|
||||
|
||||
/**
|
||||
* Popup_dialog::Refresh interface
|
||||
*/
|
||||
void refresh_popup_dialog() override { _popup_dialog.refresh(); }
|
||||
|
||||
Managed_config<Main> _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();
|
||||
|
||||
/* update depot-user selection after adding new depot URL */
|
||||
if (_system_visible)
|
||||
trigger_depot_query();
|
||||
|
||||
/*
|
||||
* The removal of an index file may have completed, re-query index
|
||||
* files to reflect this change at the depot selection menu.
|
||||
*/
|
||||
if (_popup_dialog.interested_in_file_operations())
|
||||
if (_system_visible || (_popup.state == Popup::VISIBLE))
|
||||
trigger_depot_query();
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define _TYPES_H_
|
||||
|
||||
#include <util/list_model.h>
|
||||
#include <util/xml_generator.h>
|
||||
#include <base/env.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <platform_session/platform_session.h>
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _VIEW__COMPONENT_ADD_WIDGET_H_
|
||||
#define _VIEW__COMPONENT_ADD_WIDGET_H_
|
||||
|
||||
#include <model/capacity.h>
|
||||
#include <view/index_menu_widget.h>
|
||||
#include <view/pd_route_widget.h>
|
||||
#include <view/resource_widget.h>
|
||||
@ -167,6 +168,10 @@ struct Sculpt::Component_add_widget : Widget<Vbox>
|
||||
|
||||
_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>();
|
||||
|
||||
/* select route to present routing options */
|
@ -17,6 +17,7 @@
|
||||
#include <view/dialog.h>
|
||||
#include <view/text_entry_field.h>
|
||||
#include <model/depot_url.h>
|
||||
#include <xml.h>
|
||||
|
||||
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 */
|
||||
#include <types.h>
|
||||
#include <model/launchers.h>
|
||||
#include <model/sculpt_version.h>
|
||||
#include <model/component.h>
|
||||
#include <model/runtime_config.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>
|
||||
#include <view/popup_tabs_widget.h>
|
||||
#include <view/popup_options_widget.h>
|
||||
#include <view/software_add_widget.h>
|
||||
|
||||
namespace Sculpt { struct Popup_dialog; }
|
||||
|
||||
|
||||
struct Sculpt::Popup_dialog : Dialog::Top_level_dialog
|
||||
{
|
||||
using Depot_users = Rom_data;
|
||||
using Blueprint_info = Component::Blueprint_info;
|
||||
using Depot_users = Rom_data;
|
||||
using Construction_info = Component::Construction_info;
|
||||
using Index = Software_add_widget::Index;
|
||||
|
||||
Env &_env;
|
||||
|
||||
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;
|
||||
};
|
||||
struct Action : virtual Software_add_widget::Action,
|
||||
virtual Popup_options_widget::Action { };
|
||||
|
||||
Action &_action;
|
||||
|
||||
Construction_info const &_construction_info;
|
||||
|
||||
using Route_entry = Hosted<Frame, Vbox, Frame, Vbox, Menu_entry>;
|
||||
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;
|
||||
Hosted<Frame, Vbox, Popup_tabs_widget> _tabs { Id { "tabs" } };
|
||||
Hosted<Frame, Vbox, Software_add_widget> _add;
|
||||
Hosted<Frame, Vbox, Popup_options_widget> _options;
|
||||
|
||||
void view(Scope<> &s) const override
|
||||
{
|
||||
s.sub_scope<Frame>([&] (Scope<Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Frame, Vbox> &s) {
|
||||
_depot_users.with_xml([&] (Xml_node const &users) {
|
||||
_view_menu_elements(s, users); }); }); });
|
||||
s.widget(_tabs);
|
||||
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
|
||||
{
|
||||
_launch.propagate(at, [&] {
|
||||
_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();
|
||||
});
|
||||
}
|
||||
});
|
||||
_add.propagate(at, _action);
|
||||
}
|
||||
|
||||
void drag(Dragged_at const &) override { }
|
||||
|
||||
void reset()
|
||||
void handle_key(Codepoint c, Action &action)
|
||||
{
|
||||
_state = TOP_LEVEL;
|
||||
_selected_user = User();
|
||||
_selected_route = { };
|
||||
_menu._level = 0;
|
||||
if (_tabs.add_selected())
|
||||
_add.handle_key(c, action);
|
||||
}
|
||||
|
||||
Popup_dialog(Env &env, Refresh &refresh, Action &action,
|
||||
Launchers const &launchers,
|
||||
Nic_state const &nic_state,
|
||||
Nic_target const &nic_target,
|
||||
Runtime_info const &runtime_info,
|
||||
Runtime_config const &runtime_config,
|
||||
Download_queue const &download_queue,
|
||||
Depot_users const &depot_users,
|
||||
Depot_query &depot_query,
|
||||
Construction_info const &construction_info)
|
||||
Popup_dialog(Action &action,
|
||||
Build_info const &build_info,
|
||||
Sculpt_version const &sculpt_version,
|
||||
Launchers const &launchers,
|
||||
Nic_state const &nic_state,
|
||||
Index_update_queue const &index_update_queue,
|
||||
Index const &index,
|
||||
Download_queue const &download_queue,
|
||||
Runtime_info const &runtime_info,
|
||||
Runtime_config const &runtime_config,
|
||||
Depot_users const &depot_users,
|
||||
Construction_info const &construction_info)
|
||||
:
|
||||
Top_level_dialog("popup"),
|
||||
_env(env), _launchers(launchers),
|
||||
_nic_state(nic_state), _nic_target(nic_target),
|
||||
_runtime_info(runtime_info), _runtime_config(runtime_config),
|
||||
_download_queue(download_queue), _depot_users(depot_users),
|
||||
_depot_query(depot_query), _refresh(refresh), _action(action),
|
||||
_construction_info(construction_info)
|
||||
{
|
||||
_index_rom.sigh(_index_handler);
|
||||
}
|
||||
Top_level_dialog("popup"), _action(action),
|
||||
_add(Id { "add" }, build_info, sculpt_version, nic_state,
|
||||
index_update_queue, index, download_queue, runtime_config,
|
||||
construction_info, depot_users),
|
||||
_options(Id { "options" }, runtime_info, launchers)
|
||||
{ }
|
||||
|
||||
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
|
||||
{
|
||||
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");
|
||||
});
|
||||
});
|
||||
bool keyboard_needed() const { return _tabs.add_selected()
|
||||
&& _add.keyboard_needed(); }
|
||||
|
||||
if (_state >= PKG_REQUESTED)
|
||||
_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;
|
||||
}
|
||||
void sanitize_user_selection() { _add.sanitize_user_selection(); }
|
||||
};
|
||||
|
||||
#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/index_update_queue.h>
|
||||
#include <model/index_menu.h>
|
||||
#include <model/sculpt_version.h>
|
||||
#include <view/depot_users_widget.h>
|
||||
#include <view/index_menu_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)
|
||||
pkg_selected = true; });
|
||||
|
||||
label = { Pretty(label), "(", Depot::Archive::version(pkg_path), ")",
|
||||
label = { Pretty(label), " (", Depot::Archive::version(pkg_path), ")",
|
||||
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" } };
|
||||
|
||||
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) {
|
||||
if (!attr.visible_frames)
|
||||
s.attribute("style", "invisible");
|
||||
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Frame, Vbox> &s) {
|
||||
s.widget(_users);
|
||||
|
||||
@ -163,18 +169,25 @@ struct Sculpt::Software_add_widget : Widget_interface<Vbox>
|
||||
if (_users.unfolded())
|
||||
return;
|
||||
|
||||
s.sub_scope<Vgap>();
|
||||
if (attr.visible_frames)
|
||||
s.sub_scope<Vgap>();
|
||||
|
||||
User const user = _users.selected();
|
||||
if (!_component_add_widget_visible() && !_menu.anything_visible(user))
|
||||
return;
|
||||
|
||||
bool const resource_dialog = _component_add_widget_visible();
|
||||
|
||||
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) {
|
||||
if (!attr.visible_frames)
|
||||
s.attribute("style", "invisible");
|
||||
s.sub_scope<Vbox>([&] (Scope<Vbox, Float, Frame, Vbox> &s) {
|
||||
s.sub_scope<Min_ex>(35);
|
||||
|
||||
if (_component_add_widget_visible())
|
||||
if (resource_dialog)
|
||||
_construction_info.with_construction([&] (Component const &component) {
|
||||
s.widget(_component_add, component); });
|
||||
|
Loading…
Reference in New Issue
Block a user