mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
gems: turn launcher into a panel-like application
This commit is contained in:
parent
519eb334e9
commit
755d2cce05
@ -7,7 +7,9 @@ set build_components {
|
||||
server/dynamic_rom server/nitpicker server/report_rom
|
||||
app/pointer app/menu_view
|
||||
app/scout app/launchpad app/launcher test/nitpicker
|
||||
server/nit_fader
|
||||
server/nit_fader server/rom_filter server/wm app/decorator
|
||||
app/floating_window_layouter app/status_bar server/nit_fb
|
||||
app/backdrop app/xray_trigger
|
||||
}
|
||||
|
||||
source ${genode_dir}/repos/base/run/platform_drv.inc
|
||||
@ -47,6 +49,7 @@ append_if [have_spec sdl] config {
|
||||
<service name="Input"/>
|
||||
<service name="Framebuffer"/>
|
||||
</provides>
|
||||
<config width="640" height="480"/>
|
||||
</start>}
|
||||
|
||||
append_platform_drv_config
|
||||
@ -73,20 +76,23 @@ append config {
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Nitpicker"/></provides>
|
||||
<config>
|
||||
<report xray="yes" />
|
||||
<domain name="pointer" layer="1" xray="no" origin="pointer" />
|
||||
<domain name="panel" layer="2" xray="no" />
|
||||
<domain name="" layer="3" />
|
||||
<report hover="yes" focus="yes" />
|
||||
<domain name="pointer" layer="1" content="client" label="no" origin="pointer" />
|
||||
<domain name="panel" layer="2" content="client" label="no" hover="always" focus="transient" />
|
||||
<domain name="" layer="3" color="#ff0000" hover="always" focus="click" />
|
||||
<domain name="decorator" layer="3" content="client" label="no" hover="always" focus="transient" />
|
||||
|
||||
<policy label="pointer" domain="pointer"/>
|
||||
<policy label="launcher -> menu" domain="panel"/>
|
||||
<policy label="" domain=""/>
|
||||
<policy label="pointer" domain="pointer"/>
|
||||
<policy label="wm -> launcher -> menu" domain="panel"/>
|
||||
<policy label="" domain=""/>
|
||||
<policy label="wm -> decorator" domain="decorator"/>
|
||||
<policy label="status_bar" domain="panel"/>
|
||||
|
||||
<global-key name="KEY_SCROLLLOCK" operation="xray" />
|
||||
<global-key name="KEY_SYSRQ" operation="kill" />
|
||||
<global-key name="KEY_PRINT" operation="kill" />
|
||||
<global-key name="KEY_F11" operation="kill" />
|
||||
<global-key name="KEY_F12" operation="xray" />
|
||||
<global-key name="KEY_SCROLLLOCK" label="xray_trigger" />
|
||||
<global-key name="KEY_F1" label="xray_trigger" />
|
||||
<global-key name="KEY_F2" label="xray_trigger" />
|
||||
<global-key name="KEY_LEFTMETA" label="wm -> launcher" />
|
||||
<global-key name="KEY_F3" label="wm -> launcher" />
|
||||
</config>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
@ -100,18 +106,129 @@ append config {
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="xray_trigger">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config>
|
||||
<press name="KEY_F1" xray="on"/>
|
||||
<release name="KEY_F1" xray="off"/>
|
||||
<press name="KEY_F2" xray="toggle"/>
|
||||
<hover domain="panel"/>
|
||||
<hover domain="decorator"/>
|
||||
</config>
|
||||
<route>
|
||||
<service name="ROM"> <child name="report_rom"/> </service>
|
||||
<service name="Nitpicker"> <child name="nitpicker"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="status_bar">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<route>
|
||||
<service name="ROM"> <child name="report_rom"/> </service>
|
||||
<service name="Nitpicker"> <child name="nitpicker"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config>
|
||||
<config verbose="no">
|
||||
<rom>
|
||||
<policy label="launcher -> xray" report="nitpicker -> xray"/>
|
||||
<policy label="decorator_config -> xray" report="xray_trigger -> xray"/>
|
||||
<policy label="layouter -> window_list" report="wm -> window_list"/>
|
||||
<policy label="layouter -> focus_request" report="wm -> focus_request" />
|
||||
<policy label="decorator -> window_layout" report="layouter -> window_layout"/>
|
||||
<policy label="wm -> resize_request" report="layouter -> resize_request"/>
|
||||
<policy label="decorator -> pointer" report="wm -> pointer"/>
|
||||
<policy label="layouter -> hover" report="decorator -> hover"/>
|
||||
<policy label="wm -> focus" report="layouter -> focus"/>
|
||||
<policy label="status_bar -> focus" report="nitpicker -> focus"/>
|
||||
<policy label="launcher -> focus" report="nitpicker -> focus"/>
|
||||
<policy label="xray_trigger -> hover" report="nitpicker -> hover"/>
|
||||
</rom>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="wm">
|
||||
<resource name="RAM" quantum="6M"/>
|
||||
<provides>
|
||||
<service name="Nitpicker"/>
|
||||
</provides>
|
||||
<config>
|
||||
<policy label="decorator" role="decorator"/>
|
||||
<policy label="layouter" role="layouter"/>
|
||||
<policy label="launcher -> menu" role="direct"/>
|
||||
<policy label="launcher -> testnit" role="direct"/>
|
||||
</config>
|
||||
<route>
|
||||
<any-service>
|
||||
<child name="nitpicker"/> <child name="report_rom"/> <parent/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="layouter">
|
||||
<binary name="floating_window_layouter"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<route>
|
||||
<any-service>
|
||||
<child name="wm"/> <child name="report_rom"/> <parent/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="decorator">
|
||||
<binary name="decorator"/>
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<configfile name="decorator.config"/>
|
||||
<route>
|
||||
<service name="ROM" label="decorator.config">
|
||||
<child name="decorator_config"/> </service>
|
||||
<any-service>
|
||||
<child name="wm"/> <child name="report_rom"/> <parent/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="decorator_config">
|
||||
<binary name="rom_filter"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides><service name="ROM"/></provides>
|
||||
<config verbose="yes">
|
||||
|
||||
<input name="xray_enabled" rom="xray" node="xray">
|
||||
<attribute name="enabled" />
|
||||
</input>
|
||||
|
||||
<output node="config">
|
||||
<inline>
|
||||
<controls>
|
||||
<title/> <minimizer/> <maximizer/> <closer/>
|
||||
</controls>
|
||||
</inline>
|
||||
<if>
|
||||
<has_value input="xray_enabled" value="yes" />
|
||||
<then>
|
||||
<inline>
|
||||
<policy label="" color="#ff0000" gradient="75" />
|
||||
</inline>
|
||||
</then>
|
||||
</if>
|
||||
</output>
|
||||
|
||||
</config>
|
||||
<route>
|
||||
<service name="ROM"> <child name="report_rom"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="launcher">
|
||||
<resource name="RAM" quantum="60M" />
|
||||
<config visibility="xray">
|
||||
<config focus_prefix="wm -> launcher -> ">
|
||||
<subsystem name="scout" title="Scout">
|
||||
<resource name="RAM" quantum="20M" />
|
||||
<binary name="scout" />
|
||||
@ -136,20 +253,28 @@ append config {
|
||||
<resource name="RAM" quantum="2M" />
|
||||
<binary name="testnit" />
|
||||
</subsystem>
|
||||
<subsystem name="testnit5">
|
||||
<subsystem name="testnit5" title="Nitpicker Test5">
|
||||
<resource name="RAM" quantum="2M" />
|
||||
<binary name="testnit" />
|
||||
</subsystem>
|
||||
<subsystem name="testnit6">
|
||||
<resource name="RAM" quantum="2M" />
|
||||
<binary name="testnit" />
|
||||
<subsystem name="backdrop" title="Backdrop">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<binary name="backdrop" />
|
||||
<config>
|
||||
<libc>
|
||||
<vfs>
|
||||
</vfs>
|
||||
</libc>
|
||||
<fill color="#224433" />
|
||||
</config>
|
||||
</subsystem>
|
||||
|
||||
</config>
|
||||
<route>
|
||||
<service name="ROM"> <if-arg key="label" value="xray"/>
|
||||
<service name="ROM"> <if-arg key="label" value="focus"/>
|
||||
<child name="report_rom" />
|
||||
</service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
<any-service> <child name="wm"/> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config>}
|
||||
@ -166,7 +291,8 @@ set boot_modules {
|
||||
ld.lib.so libpng.lib.so libc.lib.so libm.lib.so zlib.lib.so
|
||||
menu_view_styles.tar
|
||||
scout launchpad testnit
|
||||
nit_fader report_rom launcher
|
||||
nit_fader report_rom launcher rom_filter xray_trigger
|
||||
decorator wm floating_window_layouter status_bar nit_fb backdrop
|
||||
}
|
||||
|
||||
# platform-specific modules
|
||||
|
@ -93,6 +93,8 @@ class Launcher::Context_dialog : Input_event_handler, Dialog_generator,
|
||||
|
||||
Fading_dialog _dialog;
|
||||
|
||||
bool _open = false;
|
||||
|
||||
unsigned _key_cnt = 0;
|
||||
Label _clicked;
|
||||
bool _click_in_progress = false;
|
||||
@ -166,8 +168,23 @@ class Launcher::Context_dialog : Input_event_handler, Dialog_generator,
|
||||
*/
|
||||
bool handle_input_event(Input::Event const &ev) override
|
||||
{
|
||||
if (ev.type() == Input::Event::MOTION)
|
||||
if (ev.type() == Input::Event::MOTION) {
|
||||
|
||||
/*
|
||||
* Re-enable the visibility of the menu if we detect motion
|
||||
* events over the menu. This way, it reappears in situations
|
||||
* where the pointer temporarily leaves the view and returns.
|
||||
*/
|
||||
if (_open)
|
||||
visible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::LEAVE) {
|
||||
visible(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::PRESS) _key_cnt++;
|
||||
if (ev.type() == Input::Event::RELEASE) _key_cnt--;
|
||||
@ -210,8 +227,12 @@ class Launcher::Context_dialog : Input_event_handler, Dialog_generator,
|
||||
|
||||
void visible(bool visible)
|
||||
{
|
||||
if (visible == _dialog.visible())
|
||||
return;
|
||||
|
||||
/* reset touch state when (re-)opening the context dialog */
|
||||
if (visible) {
|
||||
_open = true;
|
||||
_touch("");
|
||||
_reset_hover();
|
||||
dialog_changed();
|
||||
@ -221,6 +242,12 @@ class Launcher::Context_dialog : Input_event_handler, Dialog_generator,
|
||||
_dialog.visible(visible);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
_open = false;
|
||||
visible(false);
|
||||
}
|
||||
|
||||
void position(Fading_dialog::Position position)
|
||||
{
|
||||
_dialog.position(position);
|
||||
|
@ -192,6 +192,7 @@ class Launcher::Fading_dialog : private Input_event_handler
|
||||
Nit_fader_slave _nit_fader_slave;
|
||||
Menu_view_slave _menu_view_slave;
|
||||
|
||||
bool _visible = false;
|
||||
|
||||
public:
|
||||
|
||||
@ -246,7 +247,13 @@ class Launcher::Fading_dialog : private Input_event_handler
|
||||
});
|
||||
}
|
||||
|
||||
void visible(bool visible) { _nit_fader_slave.visible(visible); }
|
||||
void visible(bool visible)
|
||||
{
|
||||
_nit_fader_slave.visible(visible);
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
bool visible() const { return _visible; }
|
||||
|
||||
void position(Position position) { _menu_view_slave.position(position); }
|
||||
};
|
||||
|
@ -21,115 +21,154 @@
|
||||
#include <nitpicker_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <menu_dialog.h>
|
||||
#include <panel_dialog.h>
|
||||
|
||||
|
||||
namespace Launcher { struct Main; }
|
||||
|
||||
struct Launcher::Main
|
||||
{
|
||||
Server::Entrypoint ep;
|
||||
Server::Entrypoint _ep;
|
||||
|
||||
Genode::Cap_connection cap;
|
||||
Genode::Cap_connection _cap;
|
||||
|
||||
char const *report_rom_config =
|
||||
char const *_report_rom_config =
|
||||
"<config> <rom>"
|
||||
" <policy label=\"menu_dialog\" report=\"menu_dialog\"/>"
|
||||
" <policy label=\"menu_hover\" report=\"menu_hover\"/>"
|
||||
" <policy label=\"panel_dialog\" report=\"panel_dialog\"/>"
|
||||
" <policy label=\"panel_hover\" report=\"panel_hover\"/>"
|
||||
" <policy label=\"context_dialog\" report=\"context_dialog\"/>"
|
||||
" <policy label=\"context_hover\" report=\"context_hover\"/>"
|
||||
"</rom> </config>";
|
||||
|
||||
Report_rom_slave report_rom_slave = { cap, *env()->ram_session(), report_rom_config };
|
||||
|
||||
Report_rom_slave _report_rom_slave = { _cap, *env()->ram_session(), _report_rom_config };
|
||||
|
||||
/**
|
||||
* Nitpicker session used to perform session-control operations on the
|
||||
* subsystem's nitpicker sessions.
|
||||
* subsystem's nitpicker sessions and to receive global keyboard
|
||||
* shortcuts.
|
||||
*/
|
||||
Nitpicker::Connection nitpicker;
|
||||
Nitpicker::Connection _nitpicker;
|
||||
|
||||
Genode::Attached_dataspace _input_ds { _nitpicker.input()->dataspace() };
|
||||
|
||||
Input::Event const *_ev_buf() { return _input_ds.local_addr<Input::Event>(); }
|
||||
|
||||
Genode::Signal_rpc_member<Main> _input_dispatcher =
|
||||
{ _ep, *this, &Main::_handle_input };
|
||||
|
||||
void _handle_input(unsigned);
|
||||
|
||||
unsigned _key_cnt = 0;
|
||||
|
||||
Genode::Signal_rpc_member<Main> _exited_child_dispatcher =
|
||||
{ ep, *this, &Main::_handle_exited_child };
|
||||
{ _ep, *this, &Main::_handle_exited_child };
|
||||
|
||||
Subsystem_manager subsystem_manager { ep, cap, _exited_child_dispatcher };
|
||||
Subsystem_manager _subsystem_manager { _ep, _cap, _exited_child_dispatcher };
|
||||
|
||||
Menu_dialog menu_dialog { ep, cap, *env()->ram_session(), report_rom_slave,
|
||||
subsystem_manager, nitpicker };
|
||||
Panel_dialog _panel_dialog { _ep, _cap, *env()->ram_session(), *env()->heap(),
|
||||
_report_rom_slave, _subsystem_manager, _nitpicker };
|
||||
|
||||
|
||||
Lazy_volatile_object<Attached_rom_dataspace> xray_rom_ds;
|
||||
|
||||
enum Visibility { VISIBILITY_ALWAYS, VISIBILITY_XRAY };
|
||||
|
||||
Visibility visibility = VISIBILITY_ALWAYS;
|
||||
|
||||
void handle_config(unsigned);
|
||||
|
||||
Genode::Signal_rpc_member<Main> xray_update_dispatcher =
|
||||
{ ep, *this, &Main::handle_xray_update };
|
||||
|
||||
void handle_xray_update(unsigned);
|
||||
void _handle_config(unsigned);
|
||||
|
||||
void _handle_exited_child(unsigned)
|
||||
{
|
||||
auto kill_child_fn = [&] (Child_base::Label label) { menu_dialog.kill(label); };
|
||||
auto kill_child_fn = [&] (Child_base::Label label) { _panel_dialog.kill(label); };
|
||||
|
||||
subsystem_manager.for_each_exited_child(kill_child_fn);
|
||||
_subsystem_manager.for_each_exited_child(kill_child_fn);
|
||||
}
|
||||
|
||||
Label _focus_prefix;
|
||||
|
||||
Genode::Attached_rom_dataspace _focus_rom { "focus" };
|
||||
|
||||
void _handle_focus_update(unsigned);
|
||||
|
||||
Genode::Signal_rpc_member<Main> _focus_update_dispatcher =
|
||||
{ _ep, *this, &Main::_handle_focus_update };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Main(Server::Entrypoint &ep) : ep(ep)
|
||||
Main(Server::Entrypoint &ep) : _ep(ep)
|
||||
{
|
||||
handle_config(0);
|
||||
_nitpicker.input()->sigh(_input_dispatcher);
|
||||
_focus_rom.sigh(_focus_update_dispatcher);
|
||||
|
||||
if (visibility == VISIBILITY_ALWAYS)
|
||||
menu_dialog.visible(true);
|
||||
_handle_config(0);
|
||||
|
||||
_panel_dialog.visible(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Launcher::Main::handle_config(unsigned)
|
||||
void Launcher::Main::_handle_config(unsigned)
|
||||
{
|
||||
config()->reload();
|
||||
|
||||
/* set default visibility */
|
||||
visibility = VISIBILITY_ALWAYS;
|
||||
_focus_prefix = config()->xml_node().attribute_value("focus_prefix", Label());
|
||||
|
||||
/* obtain model about nitpicker's xray mode */
|
||||
if (config()->xml_node().has_attribute("visibility")) {
|
||||
if (config()->xml_node().attribute("visibility").has_value("xray")) {
|
||||
xray_rom_ds.construct("xray");
|
||||
xray_rom_ds->sigh(xray_update_dispatcher);
|
||||
|
||||
visibility = VISIBILITY_XRAY;
|
||||
|
||||
/* manually import the initial xray state */
|
||||
handle_xray_update(0);
|
||||
}
|
||||
}
|
||||
|
||||
menu_dialog.update();
|
||||
_panel_dialog.update(config()->xml_node());
|
||||
}
|
||||
|
||||
|
||||
void Launcher::Main::handle_xray_update(unsigned)
|
||||
void Launcher::Main::_handle_input(unsigned)
|
||||
{
|
||||
xray_rom_ds->update();
|
||||
if (!xray_rom_ds->is_valid()) {
|
||||
PWRN("could not access xray info");
|
||||
menu_dialog.visible(false);
|
||||
return;
|
||||
unsigned const num_ev = _nitpicker.input()->flush();
|
||||
|
||||
for (unsigned i = 0; i < num_ev; i++) {
|
||||
|
||||
Input::Event const &e = _ev_buf()[i];
|
||||
|
||||
if (e.type() == Input::Event::PRESS) _key_cnt++;
|
||||
if (e.type() == Input::Event::RELEASE) _key_cnt--;
|
||||
|
||||
/*
|
||||
* The _key_cnt can become 2 only when the global key (as configured
|
||||
* in the nitpicker config) is pressed together with another key.
|
||||
* Hence, the following condition triggers on key combinations with
|
||||
* the global modifier key, whatever the global modifier key is.
|
||||
*/
|
||||
if (e.type() == Input::Event::PRESS && _key_cnt == 2) {
|
||||
|
||||
if (e.keycode() == Input::KEY_TAB)
|
||||
_panel_dialog.focus_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Xml_node xray(xray_rom_ds->local_addr<char>());
|
||||
|
||||
bool const visible = xray.has_attribute("enabled")
|
||||
&& xray.attribute("enabled").has_value("yes");
|
||||
void Launcher::Main::_handle_focus_update(unsigned)
|
||||
{
|
||||
try {
|
||||
_focus_rom.update();
|
||||
|
||||
menu_dialog.visible(visible);
|
||||
Xml_node focus_node(_focus_rom.local_addr<char>());
|
||||
|
||||
/*
|
||||
* Propagate focus information to panel such that the focused
|
||||
* subsystem gets highlighted.
|
||||
*/
|
||||
Label label = focus_node.attribute_value("label", Label());
|
||||
|
||||
size_t const prefix_len = Genode::strlen(_focus_prefix.string());
|
||||
if (!Genode::strcmp(_focus_prefix.string(), label.string(), prefix_len)) {
|
||||
label = Label(label.string() + prefix_len);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* A foreign nitpicker client not started by ourself has the focus.
|
||||
*/
|
||||
label = Label();
|
||||
}
|
||||
|
||||
_panel_dialog.focus_changed(label);
|
||||
|
||||
} catch (...) {
|
||||
PWRN("no focus model available");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,11 +26,21 @@ namespace Launcher { struct Menu_dialog; }
|
||||
|
||||
|
||||
class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
Hover_handler, Dialog_model,
|
||||
Context_dialog::Response_handler
|
||||
Hover_handler, Dialog_model
|
||||
{
|
||||
public:
|
||||
|
||||
struct Response_handler
|
||||
{
|
||||
virtual void handle_selection(Label const &) = 0;
|
||||
virtual void handle_menu_leave() = 0;
|
||||
virtual void handle_menu_motion() = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Response_handler &_response_handler;
|
||||
|
||||
typedef String<128> Title;
|
||||
|
||||
struct Element : List<Element>::Element
|
||||
@ -58,7 +68,7 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
xml.node("button", [&] () {
|
||||
xml.attribute("name", e->label.string());
|
||||
|
||||
if ((e->hovered && !_click_in_progress)
|
||||
if ((e->hovered)
|
||||
|| (e->hovered && e->touched))
|
||||
xml.attribute("hovered", "yes");
|
||||
|
||||
@ -72,43 +82,15 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
}
|
||||
}
|
||||
|
||||
class Lookup_failed { };
|
||||
Fading_dialog::Position _position { 0 - 4, 28 - 4 };
|
||||
|
||||
Element const &_lookup_const(Label const &label) const
|
||||
{
|
||||
for (Element const *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
return *e;
|
||||
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Element &_lookup(Label const &label)
|
||||
{
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
return *e;
|
||||
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Fading_dialog::Position _position { 32, 32 };
|
||||
|
||||
Timer::Connection _timer;
|
||||
Subsystem_manager &_subsystem_manager;
|
||||
Nitpicker::Session &_nitpicker;
|
||||
Fading_dialog _dialog;
|
||||
Fading_dialog _dialog;
|
||||
|
||||
Rect _hovered_rect;
|
||||
|
||||
bool _open = false;
|
||||
|
||||
unsigned _key_cnt = 0;
|
||||
Label _clicked;
|
||||
bool _click_in_progress = false;
|
||||
|
||||
Signal_rpc_member<Menu_dialog> _timer_dispatcher;
|
||||
|
||||
Label _context_subsystem;
|
||||
Context_dialog _context_dialog;
|
||||
|
||||
Label _hovered() const
|
||||
{
|
||||
@ -119,102 +101,17 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
return Label("");
|
||||
}
|
||||
|
||||
bool _running(Label const &label) const
|
||||
{
|
||||
try { return _lookup_const(label).running; }
|
||||
catch (Lookup_failed) { return false; }
|
||||
}
|
||||
|
||||
void _running(Label const &label, bool running)
|
||||
{
|
||||
try { _lookup(label).running = running; }
|
||||
catch (Lookup_failed) { }
|
||||
}
|
||||
|
||||
void _touch(Label const &label)
|
||||
{
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
e->touched = (e->label == label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup subsystem in config
|
||||
*/
|
||||
static Xml_node _subsystem(Xml_node config, char const *name)
|
||||
{
|
||||
Xml_node node = config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
if (node.attribute("name").has_value(name))
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void _start(Label const &label)
|
||||
{
|
||||
try {
|
||||
_subsystem_manager.start(_subsystem(config()->xml_node(),
|
||||
label.string()));
|
||||
_running(label, true);
|
||||
|
||||
dialog_changed();
|
||||
|
||||
} catch (Xml_node::Nonexistent_sub_node) {
|
||||
PERR("no subsystem config found for \"%s\"", label.string());
|
||||
} catch (Subsystem_manager::Invalid_config) {
|
||||
PERR("invalid subsystem configuration for \"%s\"", label.string());
|
||||
}
|
||||
}
|
||||
|
||||
void _kill(Label const &label)
|
||||
{
|
||||
_subsystem_manager.kill(label.string());
|
||||
_running(label, false);
|
||||
dialog_changed();
|
||||
_dialog.update();
|
||||
|
||||
_context_dialog.visible(false);
|
||||
}
|
||||
|
||||
void _hide(Label const &label)
|
||||
{
|
||||
_nitpicker.session_control(selector(label.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_HIDE);
|
||||
|
||||
_context_dialog.visible(false);
|
||||
}
|
||||
|
||||
void _handle_timer(unsigned)
|
||||
{
|
||||
if (_click_in_progress && _hovered() == _clicked) {
|
||||
|
||||
_touch("");
|
||||
|
||||
Fading_dialog::Position position(_hovered_rect.p2().x(),
|
||||
_hovered_rect.p1().y() - 44);
|
||||
_context_subsystem = _clicked;
|
||||
_context_dialog.position(_position + position);
|
||||
_context_dialog.visible(true);
|
||||
}
|
||||
|
||||
_click_in_progress = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Menu_dialog(Server::Entrypoint &ep, Cap_session &cap, Ram_session &ram,
|
||||
Report_rom_slave &report_rom_slave,
|
||||
Subsystem_manager &subsystem_manager,
|
||||
Nitpicker::Session &nitpicker)
|
||||
Response_handler &response_handler)
|
||||
:
|
||||
_subsystem_manager(subsystem_manager),
|
||||
_nitpicker(nitpicker),
|
||||
_response_handler(response_handler),
|
||||
_dialog(ep, cap, ram, report_rom_slave, "menu_dialog", "menu_hover",
|
||||
*this, *this, *this, *this,
|
||||
_position),
|
||||
_timer_dispatcher(ep, *this, &Menu_dialog::_handle_timer),
|
||||
_context_dialog(ep, cap, ram, report_rom_slave, *this)
|
||||
_position)
|
||||
{
|
||||
_timer.sigh(_timer_dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,8 +184,25 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
*/
|
||||
bool handle_input_event(Input::Event const &ev) override
|
||||
{
|
||||
if (ev.type() == Input::Event::MOTION)
|
||||
if (ev.type() == Input::Event::LEAVE) {
|
||||
_response_handler.handle_menu_leave();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::MOTION) {
|
||||
|
||||
_response_handler.handle_menu_motion();
|
||||
|
||||
/*
|
||||
* Re-enable the visibility of the menu if we detect motion
|
||||
* events over the menu. This way, it reappears in situations
|
||||
* where the pointer temporarily leaves the view and returns.
|
||||
*/
|
||||
if (_open)
|
||||
visible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::PRESS) _key_cnt++;
|
||||
if (ev.type() == Input::Event::RELEASE) _key_cnt--;
|
||||
@ -297,71 +211,39 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
&& ev.keycode() == Input::BTN_LEFT
|
||||
&& _key_cnt == 1) {
|
||||
|
||||
_context_dialog.visible(false);
|
||||
|
||||
Label const hovered = _hovered();
|
||||
|
||||
_click_in_progress = true;
|
||||
_clicked = hovered;
|
||||
_touch(hovered);
|
||||
|
||||
enum { CONTEXT_DELAY = 500 };
|
||||
|
||||
if (_running(hovered)) {
|
||||
_nitpicker.session_control(selector(hovered.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_TO_FRONT);
|
||||
_nitpicker.session_control(selector(hovered.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_SHOW);
|
||||
_timer.trigger_once(CONTEXT_DELAY*1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::RELEASE
|
||||
&& _click_in_progress && _key_cnt == 0) {
|
||||
|
||||
Label const hovered = _hovered();
|
||||
|
||||
if (_clicked == hovered) {
|
||||
|
||||
if (!_running(hovered))
|
||||
_start(hovered);
|
||||
}
|
||||
|
||||
_touch("");
|
||||
_clicked = Label("");
|
||||
_click_in_progress = false;
|
||||
_response_handler.handle_selection(_hovered());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_context_kill() override
|
||||
{
|
||||
_kill(_context_subsystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Context_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_context_hide() override
|
||||
{
|
||||
_hide(_context_subsystem);
|
||||
}
|
||||
|
||||
void visible(bool visible)
|
||||
{
|
||||
if (visible == _dialog.visible())
|
||||
return;
|
||||
|
||||
_dialog.visible(visible);
|
||||
|
||||
if (!visible)
|
||||
_context_dialog.visible(false);
|
||||
if (visible)
|
||||
_open = true;
|
||||
}
|
||||
|
||||
void kill(Child_base::Label const &label) { _kill(label); }
|
||||
void close()
|
||||
{
|
||||
_open = false;
|
||||
visible(false);
|
||||
}
|
||||
|
||||
void update()
|
||||
void running(Label const &label, bool running)
|
||||
{
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
e->running = running;
|
||||
|
||||
_dialog.update();
|
||||
}
|
||||
|
||||
void update(Xml_node subsystems)
|
||||
{
|
||||
if (_elements.first()) {
|
||||
PERR("subsequent updates are not supported");
|
||||
@ -370,8 +252,6 @@ class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
|
||||
|
||||
Element *last = nullptr;
|
||||
|
||||
Xml_node subsystems = config()->xml_node();
|
||||
|
||||
subsystems.for_each_sub_node("subsystem",
|
||||
[&] (Xml_node subsystem)
|
||||
{
|
||||
|
@ -131,7 +131,7 @@ class Launcher::Menu_view_slave
|
||||
Genode::size_t const _ep_stack_size = 4*1024*sizeof(Genode::addr_t);
|
||||
Genode::Rpc_entrypoint _ep;
|
||||
Policy _policy;
|
||||
Genode::size_t const _quota = 4*1024*1024;
|
||||
Genode::size_t const _quota = 6*1024*1024;
|
||||
Genode::Slave _slave;
|
||||
|
||||
public:
|
||||
|
554
repos/gems/src/app/launcher/panel_dialog.h
Normal file
554
repos/gems/src/app/launcher/panel_dialog.h
Normal file
@ -0,0 +1,554 @@
|
||||
/*
|
||||
* \brief Panel dialog
|
||||
* \author Norman Feske
|
||||
* \date 2015-10-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _PANEL_DIALOG_H_
|
||||
#define _PANEL_DIALOG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <fading_dialog.h>
|
||||
#include <subsystem_manager.h>
|
||||
#include <context_dialog.h>
|
||||
#include <menu_dialog.h>
|
||||
|
||||
namespace Launcher { struct Panel_dialog; }
|
||||
|
||||
|
||||
class Launcher::Panel_dialog : Input_event_handler, Dialog_generator,
|
||||
Hover_handler, Dialog_model,
|
||||
Context_dialog::Response_handler,
|
||||
Menu_dialog::Response_handler
|
||||
{
|
||||
private:
|
||||
|
||||
typedef String<128> Title;
|
||||
|
||||
struct Element : List<Element>::Element
|
||||
{
|
||||
Label const label;
|
||||
Title const title;
|
||||
|
||||
bool hovered = false;
|
||||
bool touched = false;
|
||||
bool selected = false;
|
||||
|
||||
Element(Label label, Title title) : label(label), title(title)
|
||||
{ }
|
||||
};
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
|
||||
List<Element> _elements;
|
||||
|
||||
Label _focus;
|
||||
|
||||
static char const *_menu_button_label() { return "_menu"; }
|
||||
|
||||
Element _menu_button { _menu_button_label(), "Menu" };
|
||||
|
||||
bool _is_focused(Element const &e)
|
||||
{
|
||||
size_t const label_len = strlen(e.label.string());
|
||||
|
||||
if (strcmp(e.label.string(), _focus.string(), label_len))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Even when the strcmp suceeded, the element's label might
|
||||
* not match the focus. E.g., if two subsystems "scout" and
|
||||
* "scoutx" are present the focus of "scoutx" would match both
|
||||
* subsystem labels because the strcmp is limited to the length
|
||||
* of the subsystem label. Hence, we need to make sure that
|
||||
* the focus matched at a separator boundary.
|
||||
*/
|
||||
char const *char_after_label = _focus.string() + label_len;
|
||||
|
||||
if (*char_after_label == 0 || !strcmp(" -> ", char_after_label, 4))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void _generate_dialog_element(Xml_generator &xml, Element const &e)
|
||||
{
|
||||
xml.node("button", [&] () {
|
||||
xml.attribute("name", e.label.string());
|
||||
|
||||
if (&e != &_menu_button)
|
||||
xml.attribute("style", "subdued");
|
||||
|
||||
if ((e.hovered && !_click_in_progress)
|
||||
|| (e.hovered && e.touched))
|
||||
xml.attribute("hovered", "yes");
|
||||
|
||||
if (e.selected || e.touched || _is_focused(e))
|
||||
xml.attribute("selected", "yes");
|
||||
|
||||
xml.node("label", [&] () {
|
||||
xml.attribute("text", e.title.string());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class Lookup_failed { };
|
||||
|
||||
Element const &_lookup_const(Label const &label) const
|
||||
{
|
||||
for (Element const *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
return *e;
|
||||
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Element &_lookup(Label const &label)
|
||||
{
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
return *e;
|
||||
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Fading_dialog::Position _position { 0, 0 };
|
||||
|
||||
Timer::Connection _timer;
|
||||
Subsystem_manager &_subsystem_manager;
|
||||
Nitpicker::Session &_nitpicker;
|
||||
Fading_dialog _dialog;
|
||||
|
||||
Rect _hovered_rect;
|
||||
|
||||
unsigned _key_cnt = 0;
|
||||
Element *_clicked = nullptr;
|
||||
bool _click_in_progress = false;
|
||||
|
||||
Signal_rpc_member<Panel_dialog> _timer_dispatcher;
|
||||
|
||||
Label _context_subsystem;
|
||||
Context_dialog _context_dialog;
|
||||
|
||||
Menu_dialog _menu_dialog;
|
||||
|
||||
Element *_hovered()
|
||||
{
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
if (e->hovered)
|
||||
return e;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup subsystem in config
|
||||
*/
|
||||
static Xml_node _subsystem(Xml_node config, char const *name)
|
||||
{
|
||||
Xml_node node = config.sub_node("subsystem");
|
||||
for (;; node = node.next("subsystem")) {
|
||||
if (node.attribute("name").has_value(name))
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void _start(Label const &label)
|
||||
{
|
||||
try {
|
||||
Xml_node subsystem = _subsystem(config()->xml_node(),
|
||||
label.string());
|
||||
_subsystem_manager.start(subsystem);
|
||||
|
||||
Title const title = subsystem.attribute_value("title", Title());
|
||||
|
||||
Element *e = new (_alloc) Element(label, title);
|
||||
|
||||
/* find last element of the list */
|
||||
Element *at = _elements.first();
|
||||
for (; at && at->next(); at = at->next());
|
||||
|
||||
_elements.insert(e, at);
|
||||
|
||||
dialog_changed();
|
||||
|
||||
} catch (Xml_node::Nonexistent_sub_node) {
|
||||
PERR("no subsystem config found for \"%s\"", label.string());
|
||||
} catch (Subsystem_manager::Invalid_config) {
|
||||
PERR("invalid subsystem configuration for \"%s\"", label.string());
|
||||
}
|
||||
}
|
||||
|
||||
void _kill(Label const &label)
|
||||
{
|
||||
Element &e = _lookup(label);
|
||||
|
||||
_subsystem_manager.kill(label.string());
|
||||
|
||||
_elements.remove(&e);
|
||||
|
||||
if (_clicked == &e)
|
||||
_clicked = nullptr;
|
||||
|
||||
Genode::destroy(_alloc, &e);
|
||||
|
||||
dialog_changed();
|
||||
_dialog.update();
|
||||
|
||||
_context_dialog.close();
|
||||
|
||||
_menu_dialog.running(label, false);
|
||||
}
|
||||
|
||||
void _hide(Label const &label)
|
||||
{
|
||||
_nitpicker.session_control(selector(label.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_HIDE);
|
||||
|
||||
_context_dialog.close();
|
||||
}
|
||||
|
||||
void _open_context_dialog(Label const &label)
|
||||
{
|
||||
/* reset touch state in each element */
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
e->touched = false;
|
||||
|
||||
Fading_dialog::Position position(_hovered_rect.p1().x(),
|
||||
_hovered_rect.p2().y());
|
||||
|
||||
_context_subsystem = label;
|
||||
_context_dialog.position(_position + position);
|
||||
_context_dialog.visible(true);
|
||||
}
|
||||
|
||||
void _handle_timer(unsigned)
|
||||
{
|
||||
if (_click_in_progress && _clicked && _hovered() == _clicked) {
|
||||
_open_context_dialog(_clicked->label);
|
||||
}
|
||||
_click_in_progress = false;
|
||||
}
|
||||
|
||||
void _to_front(Label const &label)
|
||||
{
|
||||
_nitpicker.session_control(selector(label.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_TO_FRONT);
|
||||
_nitpicker.session_control(selector(label.string()),
|
||||
Nitpicker::Session::SESSION_CONTROL_SHOW);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Panel_dialog(Server::Entrypoint &ep, Cap_session &cap, Ram_session &ram,
|
||||
Genode::Allocator &alloc,
|
||||
Report_rom_slave &report_rom_slave,
|
||||
Subsystem_manager &subsystem_manager,
|
||||
Nitpicker::Session &nitpicker)
|
||||
:
|
||||
_alloc(alloc),
|
||||
_subsystem_manager(subsystem_manager),
|
||||
_nitpicker(nitpicker),
|
||||
_dialog(ep, cap, ram, report_rom_slave, "panel_dialog", "panel_hover",
|
||||
*this, *this, *this, *this,
|
||||
_position),
|
||||
_timer_dispatcher(ep, *this, &Panel_dialog::_handle_timer),
|
||||
_context_dialog(ep, cap, ram, report_rom_slave, *this),
|
||||
_menu_dialog(ep, cap, ram, report_rom_slave, *this)
|
||||
{
|
||||
_elements.insert(&_menu_button);
|
||||
_timer.sigh(_timer_dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog_generator interface
|
||||
*/
|
||||
void generate_dialog(Xml_generator &xml) override
|
||||
{
|
||||
xml.node("hbox", [&] () {
|
||||
for (Element const *e = _elements.first(); e; e = e->next())
|
||||
_generate_dialog_element(xml, *e);
|
||||
});
|
||||
}
|
||||
|
||||
Rect _hovered_button_rect(Xml_node hover) const
|
||||
{
|
||||
Point p(0, 0);
|
||||
|
||||
for (;; hover = hover.sub_node()) {
|
||||
|
||||
p = p + Point(point_attribute(hover));
|
||||
|
||||
if (hover.has_type("button"))
|
||||
return Rect(p, area_attribute(hover));
|
||||
|
||||
if (!hover.num_sub_nodes())
|
||||
break;
|
||||
}
|
||||
|
||||
return Rect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hover_handler interface
|
||||
*/
|
||||
void hover_changed(Xml_node hover) override
|
||||
{
|
||||
Element *old_hovered = _hovered();
|
||||
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
e->hovered = false;
|
||||
|
||||
try {
|
||||
Xml_node button = hover.sub_node("dialog")
|
||||
.sub_node("hbox")
|
||||
.sub_node("button");
|
||||
|
||||
for (Element *e = _elements.first(); e; e = e->next()) {
|
||||
|
||||
Label const label =
|
||||
Decorator::string_attribute(button, "name", Label(""));
|
||||
|
||||
if (e->label == label) {
|
||||
e->hovered = true;
|
||||
|
||||
_hovered_rect = _hovered_button_rect(hover);
|
||||
}
|
||||
}
|
||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||
|
||||
Element *new_hovered = _hovered();
|
||||
|
||||
if (old_hovered != new_hovered)
|
||||
dialog_changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input_event_handler interface
|
||||
*/
|
||||
bool handle_input_event(Input::Event const &ev) override
|
||||
{
|
||||
if (ev.type() == Input::Event::LEAVE) {
|
||||
|
||||
/*
|
||||
* Let menu dialog disappear when the panel is unhovered. One
|
||||
* would expect that the user had no chance to select an item
|
||||
* from the menu because when entering the menu, we will no
|
||||
* longer hover the panel. However, the menu disappears slowly.
|
||||
* If the pointer moves over to the menu in a reasonable time,
|
||||
* the visiblity of the menu is re-enabled.
|
||||
*/
|
||||
_menu_dialog.visible(false);
|
||||
_context_dialog.visible(false);
|
||||
_menu_button.selected = false;
|
||||
_dialog.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::MOTION)
|
||||
return true;
|
||||
|
||||
if (ev.type() == Input::Event::PRESS) _key_cnt++;
|
||||
if (ev.type() == Input::Event::RELEASE) _key_cnt--;
|
||||
|
||||
if (ev.type() == Input::Event::PRESS
|
||||
&& ev.keycode() == Input::BTN_LEFT
|
||||
&& _key_cnt == 1) {
|
||||
|
||||
_context_dialog.visible(false);
|
||||
|
||||
Element *hovered = _hovered();
|
||||
|
||||
_click_in_progress = true;
|
||||
_clicked = hovered;
|
||||
|
||||
if (!hovered)
|
||||
return false;
|
||||
|
||||
hovered->touched = true;
|
||||
|
||||
if (hovered == &_menu_button) {
|
||||
|
||||
/* menu button presses */
|
||||
if (_menu_button.selected)
|
||||
_menu_dialog.close();
|
||||
else
|
||||
_menu_dialog.visible(true);
|
||||
|
||||
_menu_button.selected = !_menu_button.selected;
|
||||
_dialog.update();
|
||||
return false;
|
||||
}
|
||||
|
||||
_menu_dialog.close();
|
||||
|
||||
_to_front(hovered->label);
|
||||
|
||||
/*
|
||||
* Open the context dialog after the user keeps pressing the
|
||||
* button for a while.
|
||||
*/
|
||||
enum { CONTEXT_DELAY = 500 };
|
||||
_timer.trigger_once(CONTEXT_DELAY*1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open context dialog on right click
|
||||
*/
|
||||
if (ev.type() == Input::Event::PRESS
|
||||
&& ev.keycode() == Input::BTN_RIGHT
|
||||
&& _key_cnt == 1) {
|
||||
|
||||
Element *hovered = _hovered();
|
||||
|
||||
if (hovered && hovered != &_menu_button)
|
||||
_open_context_dialog(hovered->label);
|
||||
}
|
||||
|
||||
if (ev.type() == Input::Event::RELEASE
|
||||
&& _click_in_progress && _key_cnt == 0) {
|
||||
|
||||
Element *hovered = _hovered();
|
||||
|
||||
if (hovered)
|
||||
hovered->touched = false;
|
||||
|
||||
_clicked = nullptr;
|
||||
_click_in_progress = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_context_kill() override
|
||||
{
|
||||
_kill(_context_subsystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Context_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_context_hide() override
|
||||
{
|
||||
_hide(_context_subsystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_menu_motion()
|
||||
{
|
||||
_menu_button.selected = true;
|
||||
_dialog.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_menu_leave()
|
||||
{
|
||||
/* XXX eventually revert the state of the menu button */
|
||||
_menu_button.selected = false;
|
||||
|
||||
_dialog.update();
|
||||
|
||||
_menu_dialog.visible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu_dialog::Response_handler interface
|
||||
*/
|
||||
void handle_selection(Label const &label) override
|
||||
{
|
||||
/*
|
||||
* If subsystem of the specified label is already running, ignore
|
||||
* the click.
|
||||
*/
|
||||
bool already_running = false;
|
||||
for (Element *e = _elements.first(); e; e = e->next())
|
||||
if (e->label == label)
|
||||
already_running = true;
|
||||
|
||||
if (already_running) {
|
||||
_to_front(label);
|
||||
|
||||
} else {
|
||||
|
||||
_start(label);
|
||||
|
||||
_dialog.update();
|
||||
|
||||
/* propagate running state of subsystem to menu dialog */
|
||||
_menu_dialog.running(label, true);
|
||||
}
|
||||
|
||||
/* let menu disappear */
|
||||
_menu_dialog.close();
|
||||
}
|
||||
|
||||
void visible(bool visible)
|
||||
{
|
||||
_dialog.visible(visible);
|
||||
|
||||
if (!visible)
|
||||
_context_dialog.visible(false);
|
||||
}
|
||||
|
||||
void kill(Child_base::Label const &label)
|
||||
{
|
||||
_kill(label);
|
||||
}
|
||||
|
||||
void update(Xml_node config)
|
||||
{
|
||||
/* populate menu dialog with one item per subsystem */
|
||||
_menu_dialog.update(config);
|
||||
|
||||
/* evaluate configuration */
|
||||
|
||||
_dialog.update();
|
||||
}
|
||||
|
||||
void focus_changed(Label const &label)
|
||||
{
|
||||
_focus = label;
|
||||
|
||||
_dialog.update();
|
||||
}
|
||||
|
||||
void focus_next()
|
||||
{
|
||||
/* find focused element */
|
||||
Element *e = _elements.first();
|
||||
|
||||
for (; e && !_is_focused(*e); e = e->next());
|
||||
|
||||
/* none of our subsystems is focused, start with the first one */
|
||||
if (!e)
|
||||
e = _elements.first();
|
||||
|
||||
/*
|
||||
* Determine next session in the list, if we reach the end, start
|
||||
* at the beginning (the element right after the menu button.
|
||||
*/
|
||||
Element *new_focused = e->next() ? e->next() : _menu_button.next();
|
||||
|
||||
if (new_focused)
|
||||
_to_front(new_focused->label);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _PANEL_DIALOG_H_ */
|
Loading…
Reference in New Issue
Block a user