mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-19 08:36:49 +00:00
menu_view: support multiple dialogs
This patch equips the menu-view component with the ability to present more than one dialog at a time. The dialogs must be declared in the <config> node as follows. <config> ... <dialog name="settings"/> </config For each dialog, menu view requests a dedicated ROM session labeled after the dialog name. The corresponding GUI session is also labeled as such. Note that only one hover report is generated responding to all dialogs. The hover report can be correlated with the hovered dialog by inspecting the the 'name' attribute of the hover report's <dialog> sub node. The former global config attributes 'xpos', 'ypos', 'width', 'height', 'opaque', and 'background' have become attributes of the <dialog> node. Fixes #5170
This commit is contained in:
parent
881f443bbe
commit
550dea7279
@ -309,7 +309,7 @@ install_config {
|
||||
|
||||
<start name="menu_view" caps="200">
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config xpos="200" ypos="100">
|
||||
<config>
|
||||
<report hover="yes"/>
|
||||
<libc stderr="/dev/log"/>
|
||||
<vfs>
|
||||
@ -317,10 +317,12 @@ install_config {
|
||||
<dir name="dev"> <log/> </dir>
|
||||
<dir name="fonts"> <fs label="fonts"/> </dir>
|
||||
</vfs>
|
||||
<dialog name="dialog" xpos="200" ypos="150"/>
|
||||
<dialog name="fixed_dialog" xpos="400" ypos="50" width="400"/>
|
||||
</config>
|
||||
<route>
|
||||
<service name="ROM" label="dialog"> <child name="dynamic_rom" /> </service>
|
||||
<service name="Report" label="hover"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="dialog"> <child name="dynamic_rom" /> </service>
|
||||
<service name="Report" label="hover"> <child name="report_rom"/> </service>
|
||||
<service name="File_system" label="fonts"> <child name="fonts_fs"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
@ -330,6 +332,23 @@ install_config {
|
||||
|
||||
build { app/menu_view }
|
||||
|
||||
set fd [open [run_dir]/genode/fixed_dialog w]
|
||||
puts $fd {
|
||||
<dialog>
|
||||
<frame>
|
||||
<vbox>
|
||||
<button name="perf" hovered="yes">
|
||||
<label text="Performance"/>
|
||||
</button>
|
||||
<button name="battery" selected="yes" hovered="yes">
|
||||
<label text="Battery"/>
|
||||
</button>
|
||||
</vbox>
|
||||
</frame>
|
||||
</dialog>
|
||||
}
|
||||
close $fd
|
||||
|
||||
build_boot_image [build_artifacts]
|
||||
|
||||
run_genode_until forever
|
||||
|
@ -177,6 +177,9 @@ namespace File_vault {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
xml.node("dialog", [&] {
|
||||
xml.attribute("name", "dialog"); });
|
||||
});
|
||||
|
||||
xml.node("route", [&] () {
|
||||
|
286
repos/gems/src/app/menu_view/dialog.h
Normal file
286
repos/gems/src/app/menu_view/dialog.h
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* \brief Top-level dialog
|
||||
* \author Norman Feske
|
||||
* \date 2024-04-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _DIALOG_H_
|
||||
#define _DIALOG_H_
|
||||
|
||||
/* Genode include */
|
||||
#include <input/event.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/gui_buffer.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <widget_factory.h>
|
||||
#include <root_widget.h>
|
||||
|
||||
namespace Menu_view {
|
||||
|
||||
struct Dialog;
|
||||
|
||||
using Dialogs = List_model<Dialog>;
|
||||
}
|
||||
|
||||
|
||||
struct Menu_view::Dialog : List_model<Dialog>::Element
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Widget_factory &_global_widget_factory;
|
||||
|
||||
Animator _local_animator { };
|
||||
|
||||
Widget_factory _widget_factory { _global_widget_factory.alloc,
|
||||
_global_widget_factory.styles,
|
||||
_local_animator };
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void trigger_redraw() = 0;
|
||||
virtual void hover_changed() = 0;
|
||||
virtual void observed_seq_number(Input::Seq_number) = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
|
||||
using Name = Widget::Name;
|
||||
|
||||
Name const _name;
|
||||
|
||||
static Name _name_from_attr(Xml_node const &node)
|
||||
{
|
||||
return node.attribute_value("name", Name());
|
||||
}
|
||||
|
||||
Gui::Connection _gui { _env, _name };
|
||||
|
||||
Attached_dataspace _input_ds { _env.rm(), _gui.input()->dataspace() };
|
||||
|
||||
Signal_handler<Dialog> _input_handler = {
|
||||
_env.ep(), *this, &Dialog::_handle_input};
|
||||
|
||||
void _handle_input();
|
||||
|
||||
Constructible<Gui_buffer> _buffer { };
|
||||
|
||||
Gui::Session::View_handle const _view_handle = _gui.create_view();
|
||||
|
||||
Point _position { };
|
||||
|
||||
/**
|
||||
* Last pointer position at the time of the most recent hovering report,
|
||||
* in screen coordinate space.
|
||||
*/
|
||||
Point _hovered_position { };
|
||||
|
||||
bool _hovered = false;
|
||||
bool _redraw_scheduled = false;
|
||||
|
||||
Area _configured_size { };
|
||||
Area _visible_size { };
|
||||
Rect _view_geometry { };
|
||||
|
||||
bool _opaque = false;
|
||||
Color _background_color { };
|
||||
|
||||
Area _root_widget_size() const
|
||||
{
|
||||
Area const min_size = _root_widget.min_size();
|
||||
return Area(max(_configured_size.w(), min_size.w()),
|
||||
max(_configured_size.h(), min_size.h()));
|
||||
}
|
||||
|
||||
void _update_view(Rect geometry)
|
||||
{
|
||||
if (_view_geometry.p1() == geometry.p1()
|
||||
&& _view_geometry.area() == geometry.area())
|
||||
return;
|
||||
|
||||
using Command = Gui::Session::Command;
|
||||
using View_handle = Gui::Session::View_handle;
|
||||
|
||||
_view_geometry = geometry;
|
||||
_gui.enqueue<Command::Geometry>(_view_handle, _view_geometry);
|
||||
_gui.enqueue<Command::To_front>(_view_handle, View_handle());
|
||||
_gui.execute();
|
||||
}
|
||||
|
||||
Root_widget _root_widget { _name, Widget::Unique_id { }, _widget_factory, Xml_node("<dialog/>") };
|
||||
|
||||
Attached_rom_dataspace _dialog_rom { _env, _name.string() };
|
||||
|
||||
Signal_handler<Dialog> _dialog_handler {
|
||||
_env.ep(), *this, &Dialog::_handle_dialog };
|
||||
|
||||
void _handle_dialog();
|
||||
|
||||
Dialog(Env &env, Widget_factory &widget_factory, Action &action,
|
||||
Xml_node const &node)
|
||||
:
|
||||
_env(env), _global_widget_factory(widget_factory), _action(action),
|
||||
_name(_name_from_attr(node))
|
||||
{
|
||||
_dialog_rom.sigh(_dialog_handler);
|
||||
_dialog_handler.local_submit();
|
||||
_gui.input()->sigh(_input_handler);
|
||||
}
|
||||
|
||||
Widget::Hovered hovered_widget() const
|
||||
{
|
||||
return _root_widget.hovered(_hovered_position);
|
||||
}
|
||||
|
||||
void gen_hover(Xml_generator &xml) const
|
||||
{
|
||||
if (_hovered)
|
||||
_root_widget.gen_hover_model(xml, _hovered_position);
|
||||
}
|
||||
|
||||
void redraw()
|
||||
{
|
||||
if (!_redraw_scheduled)
|
||||
return;
|
||||
|
||||
Area const size = _root_widget_size();
|
||||
|
||||
unsigned const buffer_w = _buffer.constructed() ? _buffer->size().w() : 0,
|
||||
buffer_h = _buffer.constructed() ? _buffer->size().h() : 0;
|
||||
|
||||
Area const max_size(max(buffer_w, size.w()), max(buffer_h, size.h()));
|
||||
|
||||
bool const size_increased = (max_size.w() > buffer_w)
|
||||
|| (max_size.h() > buffer_h);
|
||||
|
||||
if (!_buffer.constructed() || size_increased)
|
||||
_buffer.construct(_gui, max_size, _env.ram(), _env.rm(),
|
||||
_opaque ? Gui_buffer::Alpha::OPAQUE
|
||||
: Gui_buffer::Alpha::ALPHA,
|
||||
_background_color);
|
||||
else
|
||||
_buffer->reset_surface();
|
||||
|
||||
_root_widget.position(Point(0, 0));
|
||||
|
||||
_buffer->apply_to_surface([&] (Surface<Pixel_rgb888> &pixel,
|
||||
Surface<Pixel_alpha8> &alpha) {
|
||||
_root_widget.draw(pixel, alpha, Point(0, 0));
|
||||
});
|
||||
|
||||
_buffer->flush_surface();
|
||||
_gui.framebuffer()->refresh(0, 0, _buffer->size().w(), _buffer->size().h());
|
||||
_update_view(Rect(_position, size));
|
||||
|
||||
_redraw_scheduled = false;
|
||||
}
|
||||
|
||||
bool hovered() const { return _hovered; }
|
||||
|
||||
void animate()
|
||||
{
|
||||
bool const progress = _local_animator.active();
|
||||
|
||||
_local_animator.animate();
|
||||
|
||||
if (progress)
|
||||
_redraw_scheduled = true;
|
||||
}
|
||||
|
||||
bool animation_in_progress() const { return _local_animator.active(); }
|
||||
|
||||
bool redraw_scheduled() const { return _redraw_scheduled; }
|
||||
|
||||
/*
|
||||
* List_model
|
||||
*/
|
||||
|
||||
static bool type_matches(Xml_node const &node) { return node.has_type("dialog"); }
|
||||
|
||||
bool matches(Xml_node const &node) const { return _name_from_attr(node) == _name; }
|
||||
|
||||
void update(Xml_node const &node)
|
||||
{
|
||||
_position = Point::from_xml(node);
|
||||
_configured_size = Area ::from_xml(node);
|
||||
_opaque = node.attribute_value("opaque", false);
|
||||
_background_color = node.attribute_value("background", Color(127, 127, 127, 255));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Menu_view::Dialog::_handle_dialog()
|
||||
{
|
||||
_dialog_rom.update();
|
||||
|
||||
Xml_node const dialog = _dialog_rom.xml();
|
||||
|
||||
if (dialog.has_type("empty"))
|
||||
return;
|
||||
|
||||
_root_widget.update(dialog);
|
||||
_root_widget.size(_root_widget_size());
|
||||
|
||||
_redraw_scheduled = true;
|
||||
|
||||
_action.hover_changed();
|
||||
_action.trigger_redraw();
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::Dialog::_handle_input()
|
||||
{
|
||||
Point const orig_hovered_position = _hovered_position;
|
||||
bool const orig_hovered = _hovered;
|
||||
|
||||
bool seq_number_changed = false;
|
||||
|
||||
_gui.input()->for_each_event([&] (Input::Event const &ev) {
|
||||
|
||||
ev.handle_seq_number([&] (Input::Seq_number seq_number) {
|
||||
seq_number_changed = true;
|
||||
_action.observed_seq_number(seq_number); });
|
||||
|
||||
auto hover_at = [&] (int x, int y)
|
||||
{
|
||||
_hovered = true;
|
||||
_hovered_position = Point(x, y) - _position;
|
||||
};
|
||||
|
||||
auto unhover = [&] ()
|
||||
{
|
||||
_hovered = false;
|
||||
_hovered_position = Point();
|
||||
};
|
||||
|
||||
ev.handle_absolute_motion([&] (int x, int y) {
|
||||
hover_at(x, y); });
|
||||
|
||||
ev.handle_touch([&] (Input::Touch_id id, float x, float y) {
|
||||
if (id.value == 0)
|
||||
hover_at((int)x, (int)y); });
|
||||
|
||||
/*
|
||||
* Reset hover model when losing the focus
|
||||
*/
|
||||
if (ev.hover_leave())
|
||||
unhover();
|
||||
});
|
||||
|
||||
bool const hover_changed = orig_hovered != _hovered
|
||||
|| orig_hovered_position != _hovered_position;
|
||||
|
||||
if (hover_changed || seq_number_changed)
|
||||
_action.hover_changed();
|
||||
}
|
||||
|
||||
#endif /* _DIALOG_H_ */
|
@ -11,90 +11,35 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include "widget_factory.h"
|
||||
#include "button_widget.h"
|
||||
#include "label_widget.h"
|
||||
#include "box_layout_widget.h"
|
||||
#include "root_widget.h"
|
||||
#include "float_widget.h"
|
||||
#include "frame_widget.h"
|
||||
#include "depgraph_widget.h"
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/sleep.h>
|
||||
#include <input/event.h>
|
||||
#include <os/reporter.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/vfs.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/gui_buffer.h>
|
||||
/* local includes */
|
||||
#include <dialog.h>
|
||||
#include <button_widget.h>
|
||||
#include <label_widget.h>
|
||||
#include <box_layout_widget.h>
|
||||
#include <float_widget.h>
|
||||
#include <frame_widget.h>
|
||||
#include <depgraph_widget.h>
|
||||
|
||||
namespace Menu_view { struct Main; }
|
||||
|
||||
|
||||
struct Menu_view::Main
|
||||
struct Menu_view::Main : Dialog::Action
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Gui::Connection _gui { _env };
|
||||
|
||||
Constructible<Gui_buffer> _buffer { };
|
||||
|
||||
Gui::Session::View_handle _view_handle = _gui.create_view();
|
||||
|
||||
/**
|
||||
* Dialog position in screen coordinate space
|
||||
*/
|
||||
Point _position { };
|
||||
|
||||
/**
|
||||
* Last pointer position at the time of the most recent hovering report,
|
||||
* in screen coordinate space.
|
||||
*/
|
||||
Point _hovered_position { };
|
||||
|
||||
bool _dialog_hovered = false;
|
||||
|
||||
Area _configured_size { };
|
||||
Area _visible_size { };
|
||||
|
||||
Area _root_widget_size() const
|
||||
{
|
||||
Area const min_size = _root_widget.min_size();
|
||||
return Area(max(_configured_size.w(), min_size.w()),
|
||||
max(_configured_size.h(), min_size.h()));
|
||||
}
|
||||
|
||||
Rect _view_geometry { };
|
||||
|
||||
void _update_view(Rect geometry)
|
||||
{
|
||||
if (_view_geometry.p1() == geometry.p1()
|
||||
&& _view_geometry.area() == geometry.area())
|
||||
return;
|
||||
|
||||
/* display view behind all others */
|
||||
typedef Gui::Session::Command Command;
|
||||
typedef Gui::Session::View_handle View_handle;
|
||||
|
||||
_view_geometry = geometry;
|
||||
_gui.enqueue<Command::Geometry>(_view_handle, _view_geometry);
|
||||
_gui.enqueue<Command::To_front>(_view_handle, View_handle());
|
||||
_gui.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called on config change or mode change
|
||||
*/
|
||||
void _handle_dialog_update();
|
||||
|
||||
Signal_handler<Main> _dialog_update_handler = {
|
||||
_env.ep(), *this, &Main::_handle_dialog_update};
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Signal_handler<Main> _config_handler = {
|
||||
_env.ep(), *this, &Main::_handle_config};
|
||||
|
||||
void _handle_config();
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
Vfs::Env &_vfs_env;
|
||||
@ -104,57 +49,41 @@ struct Menu_view::Main
|
||||
Directory _styles_dir { _root_dir, "styles" };
|
||||
|
||||
Style_database _styles { _env.ep(), _env.ram(), _env.rm(), _heap,
|
||||
_fonts_dir, _styles_dir, _dialog_update_handler };
|
||||
_fonts_dir, _styles_dir, _config_handler };
|
||||
|
||||
Animator _animator { };
|
||||
Animator _global_animator { };
|
||||
|
||||
Widget_factory _widget_factory { _heap, _styles, _animator };
|
||||
Widget_factory _widget_factory { _heap, _styles, _global_animator };
|
||||
|
||||
Root_widget _root_widget { _widget_factory, Xml_node("<dialog/>"), Widget::Unique_id() };
|
||||
Dialogs _dialogs { };
|
||||
|
||||
Attached_rom_dataspace _dialog_rom { _env, "dialog" };
|
||||
|
||||
Attached_dataspace _input_ds { _env.rm(), _gui.input()->dataspace() };
|
||||
|
||||
bool _opaque = false;
|
||||
|
||||
Color _background_color { };
|
||||
|
||||
Widget::Hovered _last_reported_hovered { };
|
||||
|
||||
void _handle_config();
|
||||
|
||||
Signal_handler<Main> _config_handler = {
|
||||
_env.ep(), *this, &Main::_handle_config};
|
||||
Widget::Hovered _reported_hovered { };
|
||||
|
||||
struct Input_seq_number
|
||||
{
|
||||
Constructible<Input::Seq_number> _curr { };
|
||||
|
||||
bool _processed = false;
|
||||
bool _reported = false;
|
||||
|
||||
void update(Input::Seq_number const &seq_number)
|
||||
void update(Input::Seq_number const &seq)
|
||||
{
|
||||
_curr.construct(seq_number);
|
||||
_processed = false;
|
||||
_curr.construct(seq);
|
||||
_reported = false;
|
||||
}
|
||||
|
||||
bool changed() const { return _curr.constructed() && !_processed; }
|
||||
|
||||
void mark_as_processed() { _processed = true; }
|
||||
|
||||
void generate(Xml_generator &xml)
|
||||
void generate(Xml_generator &xml) const
|
||||
{
|
||||
if (_curr.constructed())
|
||||
xml.attribute("seq_number", _curr->value);
|
||||
}
|
||||
|
||||
void mark_as_reported() { _reported = true; }
|
||||
|
||||
bool changed() const { return !_reported; }
|
||||
|
||||
} _input_seq_number { };
|
||||
|
||||
void _handle_input();
|
||||
|
||||
Signal_handler<Main> _input_handler = {
|
||||
_env.ep(), *this, &Main::_handle_input};
|
||||
struct Frame { uint64_t count; };
|
||||
|
||||
/*
|
||||
* Timer used for animating widgets
|
||||
@ -163,7 +92,7 @@ struct Menu_view::Main
|
||||
{
|
||||
enum { PERIOD = 10 };
|
||||
|
||||
Genode::uint64_t curr_frame() const { return elapsed_ms() / PERIOD; }
|
||||
Frame curr_frame() const { return { elapsed_ms() / PERIOD }; }
|
||||
|
||||
void schedule() { trigger_once((Genode::uint64_t)Frame_timer::PERIOD*1000); }
|
||||
|
||||
@ -173,6 +102,41 @@ struct Menu_view::Main
|
||||
|
||||
void _handle_frame_timer();
|
||||
|
||||
/**
|
||||
* Dialog::Action
|
||||
*/
|
||||
void trigger_redraw() override
|
||||
{
|
||||
/*
|
||||
* If we have not processed a period for at least one frame, perform the
|
||||
* processing immediately. This way, we avoid latencies when the dialog
|
||||
* model is updated sporadically.
|
||||
*/
|
||||
Frame const curr_frame = _timer.curr_frame();
|
||||
if (curr_frame.count != _last_frame.count) {
|
||||
|
||||
if (curr_frame.count - _last_frame.count > 10)
|
||||
_last_frame = curr_frame;
|
||||
|
||||
_handle_frame_timer();
|
||||
} else {
|
||||
_timer.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog::Action
|
||||
*/
|
||||
void hover_changed() override { _update_hover_report(); }
|
||||
|
||||
/**
|
||||
* Dialog::Action
|
||||
*/
|
||||
void observed_seq_number(Input::Seq_number const seq) override
|
||||
{
|
||||
_input_seq_number.update(seq);
|
||||
}
|
||||
|
||||
Signal_handler<Main> _frame_timer_handler = {
|
||||
_env.ep(), *this, &Main::_handle_frame_timer};
|
||||
|
||||
@ -180,17 +144,15 @@ struct Menu_view::Main
|
||||
|
||||
void _update_hover_report();
|
||||
|
||||
bool _schedule_redraw = false;
|
||||
|
||||
/**
|
||||
* Frame of last call of 'handle_frame_timer'
|
||||
*/
|
||||
Genode::uint64_t _last_frame = 0;
|
||||
Frame _last_frame { };
|
||||
|
||||
/**
|
||||
* Number of frames between two redraws
|
||||
*/
|
||||
enum { REDRAW_PERIOD = 2 };
|
||||
static constexpr unsigned REDRAW_PERIOD = 2;
|
||||
|
||||
/**
|
||||
* Counter used for triggering redraws. Incremented in each frame-timer
|
||||
@ -203,15 +165,10 @@ struct Menu_view::Main
|
||||
:
|
||||
_env(env), _vfs_env(libc_vfs_env)
|
||||
{
|
||||
_dialog_rom.sigh(_dialog_update_handler);
|
||||
_config.sigh(_config_handler);
|
||||
|
||||
_gui.input()->sigh(_input_handler);
|
||||
_config_handler.local_submit(); /* apply initial configuration */
|
||||
|
||||
_timer.sigh(_frame_timer_handler);
|
||||
|
||||
/* apply initial configuration */
|
||||
_handle_config();
|
||||
}
|
||||
};
|
||||
|
||||
@ -221,66 +178,35 @@ void Menu_view::Main::_update_hover_report()
|
||||
if (!_hover_reporter.constructed())
|
||||
return;
|
||||
|
||||
if (!_dialog_hovered) {
|
||||
unsigned hovered_dialogs = 0;
|
||||
|
||||
_dialogs.for_each([&] (Dialog const &dialog) {
|
||||
|
||||
if (!dialog.hovered())
|
||||
return;
|
||||
|
||||
hovered_dialogs++;
|
||||
if (hovered_dialogs != 1)
|
||||
return;
|
||||
|
||||
Widget::Hovered const hovered = dialog.hovered_widget();
|
||||
|
||||
if ((hovered != _reported_hovered) || _input_seq_number.changed()) {
|
||||
|
||||
_hover_reporter->generate([&] (Xml_generator &xml) {
|
||||
_input_seq_number.generate(xml);
|
||||
dialog.gen_hover(xml);
|
||||
});
|
||||
_reported_hovered = hovered;
|
||||
_input_seq_number.mark_as_reported();
|
||||
}
|
||||
});
|
||||
|
||||
if (hovered_dialogs == 0)
|
||||
_hover_reporter->generate([&] (Xml_generator &) { });
|
||||
return;
|
||||
}
|
||||
|
||||
Widget::Hovered const new_hovered = _root_widget.hovered(_hovered_position);
|
||||
|
||||
bool const hover_changed = (_last_reported_hovered != new_hovered);
|
||||
|
||||
if (hover_changed || _input_seq_number.changed()) {
|
||||
|
||||
_hover_reporter->generate([&] (Xml_generator &xml) {
|
||||
_input_seq_number.generate(xml);
|
||||
_root_widget.gen_hover_model(xml, _hovered_position);
|
||||
});
|
||||
|
||||
_last_reported_hovered = new_hovered;
|
||||
_input_seq_number.mark_as_processed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Menu_view::Main::_handle_dialog_update()
|
||||
{
|
||||
_styles.flush_outdated_styles();
|
||||
|
||||
Xml_node const config = _config.xml();
|
||||
|
||||
_position = Point::from_xml(config);
|
||||
_configured_size = Area ::from_xml(config);
|
||||
|
||||
_dialog_rom.update();
|
||||
|
||||
Xml_node dialog = _dialog_rom.xml();
|
||||
|
||||
if (dialog.has_type("empty"))
|
||||
return;
|
||||
|
||||
_root_widget.update(dialog);
|
||||
_root_widget.size(_root_widget_size());
|
||||
|
||||
_update_hover_report();
|
||||
|
||||
_schedule_redraw = true;
|
||||
|
||||
/*
|
||||
* If we have not processed a period for at least one frame, perform the
|
||||
* processing immediately. This way, we avoid latencies when the dialog
|
||||
* model is updated sporadically.
|
||||
*/
|
||||
Genode::uint64_t const curr_frame = _timer.curr_frame();
|
||||
if (curr_frame != _last_frame) {
|
||||
|
||||
if (curr_frame - _last_frame > 10)
|
||||
_last_frame = curr_frame;
|
||||
|
||||
_handle_frame_timer();
|
||||
} else {
|
||||
_timer.schedule();
|
||||
}
|
||||
if (hovered_dialogs > 1)
|
||||
warning("more than one dialog unexpectedly hovered at the same time");
|
||||
}
|
||||
|
||||
|
||||
@ -288,65 +214,29 @@ void Menu_view::Main::_handle_config()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
_styles.flush_outdated_styles();
|
||||
|
||||
Xml_node const config = _config.xml();
|
||||
|
||||
config.with_optional_sub_node("report", [&] (Xml_node const &report) {
|
||||
_hover_reporter.conditional(report.attribute_value("hover", false),
|
||||
_env, "hover", "hover");
|
||||
});
|
||||
|
||||
_opaque = config.attribute_value("opaque", false);
|
||||
|
||||
_background_color = config.attribute_value("background", Color(127, 127, 127, 255));
|
||||
_env, "hover", "hover"); });
|
||||
|
||||
config.with_optional_sub_node("vfs", [&] (Xml_node const &vfs_node) {
|
||||
_vfs_env.root_dir().apply_config(vfs_node); });
|
||||
|
||||
_handle_dialog_update();
|
||||
}
|
||||
_dialogs.update_from_xml(config,
|
||||
|
||||
/* create */
|
||||
[&] (Xml_node const &node) -> Dialog & {
|
||||
return *new (_heap) Dialog(_env, _widget_factory, *this, node); },
|
||||
|
||||
void Menu_view::Main::_handle_input()
|
||||
{
|
||||
Point const orig_hovered_position = _hovered_position;
|
||||
bool const orig_dialog_hovered = _dialog_hovered;
|
||||
/* destroy */
|
||||
[&] (Dialog &d) { destroy(_heap, &d); },
|
||||
|
||||
_gui.input()->for_each_event([&] (Input::Event const &ev) {
|
||||
|
||||
ev.handle_seq_number([&] (Input::Seq_number seq_number) {
|
||||
_input_seq_number.update(seq_number); });
|
||||
|
||||
auto hover_at = [&] (int x, int y)
|
||||
{
|
||||
_dialog_hovered = true;
|
||||
_hovered_position = Point(x, y) - _position;
|
||||
};
|
||||
|
||||
auto unhover = [&] ()
|
||||
{
|
||||
_dialog_hovered = false;
|
||||
_hovered_position = Point();
|
||||
};
|
||||
|
||||
ev.handle_absolute_motion([&] (int x, int y) {
|
||||
hover_at(x, y); });
|
||||
|
||||
ev.handle_touch([&] (Input::Touch_id id, float x, float y) {
|
||||
if (id.value == 0)
|
||||
hover_at((int)x, (int)y); });
|
||||
|
||||
/*
|
||||
* Reset hover model when losing the focus
|
||||
*/
|
||||
if (ev.hover_leave())
|
||||
unhover();
|
||||
});
|
||||
|
||||
bool const hover_changed = orig_dialog_hovered != _dialog_hovered
|
||||
|| orig_hovered_position != _hovered_position;
|
||||
|
||||
if (hover_changed || _input_seq_number.changed())
|
||||
_update_hover_report();
|
||||
/* update */
|
||||
[&] (Dialog &d, Xml_node const &node) { d.update(node); }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -354,68 +244,39 @@ void Menu_view::Main::_handle_frame_timer()
|
||||
{
|
||||
_frame_cnt++;
|
||||
|
||||
Genode::uint64_t const curr_frame = _timer.curr_frame();
|
||||
Frame const curr_frame = _timer.curr_frame();
|
||||
|
||||
if (_animator.active()) {
|
||||
unsigned const passed_frames =
|
||||
max(unsigned(curr_frame.count - _last_frame.count), 4U);
|
||||
|
||||
Genode::uint64_t const passed_frames = max(curr_frame - _last_frame, 4U);
|
||||
if (passed_frames > 0)
|
||||
_dialogs.for_each([&] (Dialog &dialog) {
|
||||
for (unsigned i = 0; i < passed_frames; i++)
|
||||
dialog.animate(); });
|
||||
|
||||
if (passed_frames > 0) {
|
||||
|
||||
for (Genode::uint64_t i = 0; i < passed_frames; i++)
|
||||
_animator.animate();
|
||||
|
||||
_schedule_redraw = true;
|
||||
}
|
||||
}
|
||||
bool any_redraw_scheduled = false;
|
||||
_dialogs.for_each([&] (Dialog const &dialog) {
|
||||
any_redraw_scheduled |= dialog.redraw_scheduled(); });
|
||||
|
||||
_last_frame = curr_frame;
|
||||
|
||||
if (_schedule_redraw && _frame_cnt >= REDRAW_PERIOD) {
|
||||
bool const redraw_skipped = any_redraw_scheduled && (_frame_cnt < REDRAW_PERIOD);
|
||||
|
||||
if (!redraw_skipped) {
|
||||
_frame_cnt = 0;
|
||||
|
||||
Area const size = _root_widget_size();
|
||||
|
||||
unsigned const buffer_w = _buffer.constructed() ? _buffer->size().w() : 0,
|
||||
buffer_h = _buffer.constructed() ? _buffer->size().h() : 0;
|
||||
|
||||
Area const max_size(max(buffer_w, size.w()), max(buffer_h, size.h()));
|
||||
|
||||
bool const size_increased = (max_size.w() > buffer_w)
|
||||
|| (max_size.h() > buffer_h);
|
||||
|
||||
if (!_buffer.constructed() || size_increased)
|
||||
_buffer.construct(_gui, max_size, _env.ram(), _env.rm(),
|
||||
_opaque ? Gui_buffer::Alpha::OPAQUE
|
||||
: Gui_buffer::Alpha::ALPHA,
|
||||
_background_color);
|
||||
else
|
||||
_buffer->reset_surface();
|
||||
|
||||
_root_widget.position(Point(0, 0));
|
||||
|
||||
// XXX restrict redraw to dirty regions
|
||||
// don't perform a full dialog update
|
||||
_buffer->apply_to_surface([&] (Surface<Pixel_rgb888> &pixel,
|
||||
Surface<Pixel_alpha8> &alpha) {
|
||||
_root_widget.draw(pixel, alpha, Point(0, 0));
|
||||
});
|
||||
|
||||
_buffer->flush_surface();
|
||||
_gui.framebuffer()->refresh(0, 0, _buffer->size().w(), _buffer->size().h());
|
||||
_update_view(Rect(_position, size));
|
||||
|
||||
_schedule_redraw = false;
|
||||
_dialogs.for_each([&] (Dialog &dialog) {
|
||||
dialog.redraw(); });
|
||||
}
|
||||
|
||||
bool any_animation_in_progress = false;
|
||||
_dialogs.for_each([&] (Dialog const &dialog) {
|
||||
any_animation_in_progress |= dialog.animation_in_progress(); });
|
||||
|
||||
/*
|
||||
* Deactivate timer periods when idle, activate timer when an animation is
|
||||
* in progress or a redraw is pending.
|
||||
*/
|
||||
bool const redraw_pending = _schedule_redraw && _frame_cnt != 0;
|
||||
|
||||
if (_animator.active() || redraw_pending)
|
||||
if (any_animation_in_progress || redraw_skipped)
|
||||
_timer.schedule();
|
||||
}
|
||||
|
||||
|
@ -15,16 +15,17 @@
|
||||
#define _ROOT_WIDGET_H_
|
||||
|
||||
/* local includes */
|
||||
#include "widget.h"
|
||||
#include <widget.h>
|
||||
|
||||
namespace Menu_view { struct Root_widget; }
|
||||
|
||||
|
||||
struct Menu_view::Root_widget : Widget
|
||||
{
|
||||
Root_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
|
||||
Root_widget(Name const &name, Unique_id const &id,
|
||||
Widget_factory &factory, Xml_node const &node)
|
||||
:
|
||||
Widget(factory, node, unique_id)
|
||||
Widget(name, id, factory, node)
|
||||
{ }
|
||||
|
||||
Area animated_size() const
|
||||
|
@ -33,9 +33,9 @@ namespace Menu_view {
|
||||
|
||||
using Genode::size_t;
|
||||
|
||||
typedef Surface_base::Point Point;
|
||||
typedef Surface_base::Area Area;
|
||||
typedef Surface_base::Rect Rect;
|
||||
using Point = Surface_base::Point;
|
||||
using Area = Surface_base::Area;
|
||||
using Rect = Surface_base::Rect;
|
||||
}
|
||||
|
||||
#endif /* _TYPES_H_ */
|
||||
|
@ -56,11 +56,9 @@ class Menu_view::Widget : List_model<Widget>::Element
|
||||
|
||||
using List_model<Widget>::Element::next;
|
||||
|
||||
enum { NAME_MAX_LEN = 32 };
|
||||
typedef String<NAME_MAX_LEN> Name;
|
||||
|
||||
typedef Name Type_name;
|
||||
typedef String<10> Version;
|
||||
using Name = String<32>;
|
||||
using Version = String<10>;
|
||||
using Type_name = Name;
|
||||
|
||||
struct Unique_id
|
||||
{
|
||||
@ -205,13 +203,16 @@ class Menu_view::Widget : List_model<Widget>::Element
|
||||
Point(r.x2() - margin.right, r.y2() - margin.bottom));
|
||||
}
|
||||
|
||||
Widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
|
||||
Widget(Name const &name, Unique_id const id,
|
||||
Widget_factory &factory, Xml_node const &node)
|
||||
:
|
||||
_type_name(node.type()),
|
||||
_name(node_name(node)),
|
||||
_version(node_version(node)),
|
||||
_unique_id(unique_id),
|
||||
_factory(factory)
|
||||
_type_name(node.type()), _name(name), _version(node_version(node)),
|
||||
_unique_id(id), _factory(factory)
|
||||
{ }
|
||||
|
||||
Widget(Widget_factory &factory, Xml_node const &node, Unique_id const id)
|
||||
:
|
||||
Widget(node_name(node), id, factory, node)
|
||||
{ }
|
||||
|
||||
virtual ~Widget()
|
||||
|
Loading…
x
Reference in New Issue
Block a user