mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 07:22:25 +00:00
menu_view: use timer for animation timing
This patch removes the use of sync signals as time source. The animation phase is now timed using a timer connection as tick source while sync signals are used for scheduling the redraws. Issue #5347
This commit is contained in:
parent
ffcd08b5c7
commit
58d20c7751
@ -85,7 +85,7 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
|||||||
* or changing the selection state of a button, the transition
|
* or changing the selection state of a button, the transition
|
||||||
* must be quick to provide a responsive feel.
|
* must be quick to provide a responsive feel.
|
||||||
*/
|
*/
|
||||||
enum { SLOW = 80, MEDIUM = 40, FAST = 3 };
|
enum { SLOW = 40, MEDIUM = 20, FAST = 2 };
|
||||||
int steps = SLOW;
|
int steps = SLOW;
|
||||||
if (_hovered && !new_hovered) steps = MEDIUM;
|
if (_hovered && !new_hovered) steps = MEDIUM;
|
||||||
if (!_hovered && new_hovered) steps = FAST;
|
if (!_hovered && new_hovered) steps = FAST;
|
||||||
|
@ -122,7 +122,7 @@ class Menu_view::Cursor : List_model<Cursor>::Element
|
|||||||
|
|
||||||
void update(Xml_node const &node)
|
void update(Xml_node const &node)
|
||||||
{
|
{
|
||||||
_move_to(_position_from_xml_node(node), Steps{12});
|
_move_to(_position_from_xml_node(node), Steps{6});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,9 +47,9 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
|||||||
|
|
||||||
struct Action : Interface
|
struct Action : Interface
|
||||||
{
|
{
|
||||||
virtual void trigger_redraw() = 0;
|
|
||||||
virtual void hover_changed() = 0;
|
virtual void hover_changed() = 0;
|
||||||
virtual void observed_seq_number(Input::Seq_number) = 0;
|
virtual void observed_seq_number(Input::Seq_number) = 0;
|
||||||
|
virtual Ticks now() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Action &_action;
|
Action &_action;
|
||||||
@ -72,6 +72,15 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
|||||||
|
|
||||||
void _handle_input();
|
void _handle_input();
|
||||||
|
|
||||||
|
Signal_handler<Dialog> _gui_sync_handler {
|
||||||
|
_env.ep(), *this, &Dialog::_handle_gui_sync };
|
||||||
|
|
||||||
|
void _handle_gui_sync();
|
||||||
|
|
||||||
|
bool _gui_sync_enabled = false;
|
||||||
|
|
||||||
|
Ticks _previous_sync { };
|
||||||
|
|
||||||
Constructible<Gui_buffer> _buffer { };
|
Constructible<Gui_buffer> _buffer { };
|
||||||
|
|
||||||
Gui::View_ref _view_ref { };
|
Gui::View_ref _view_ref { };
|
||||||
@ -151,7 +160,7 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
|||||||
_root_widget.gen_hover_model(xml, _hovered_position);
|
_root_widget.gen_hover_model(xml, _hovered_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void redraw()
|
void _redraw()
|
||||||
{
|
{
|
||||||
if (!_redraw_scheduled)
|
if (!_redraw_scheduled)
|
||||||
return;
|
return;
|
||||||
@ -190,7 +199,7 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
|||||||
|
|
||||||
bool hovered() const { return _hovered; }
|
bool hovered() const { return _hovered; }
|
||||||
|
|
||||||
void animate()
|
void _animate()
|
||||||
{
|
{
|
||||||
bool const progress = _local_animator.active();
|
bool const progress = _local_animator.active();
|
||||||
|
|
||||||
@ -200,9 +209,14 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
|||||||
_redraw_scheduled = true;
|
_redraw_scheduled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool animation_in_progress() const { return _local_animator.active(); }
|
void enforce_font_sytle_change()
|
||||||
|
{
|
||||||
|
_handle_dialog();
|
||||||
|
|
||||||
bool redraw_scheduled() const { return _redraw_scheduled; }
|
/* fast-forward geometry animation */
|
||||||
|
while (_local_animator.active())
|
||||||
|
_animate();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List_model
|
* List_model
|
||||||
@ -237,7 +251,13 @@ void Menu_view::Dialog::_handle_dialog()
|
|||||||
_redraw_scheduled = true;
|
_redraw_scheduled = true;
|
||||||
|
|
||||||
_action.hover_changed();
|
_action.hover_changed();
|
||||||
_action.trigger_redraw();
|
|
||||||
|
if (!_gui_sync_enabled) {
|
||||||
|
_gui.framebuffer.sync_sigh(_gui_sync_handler);
|
||||||
|
_gui_sync_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handle_gui_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -287,4 +307,25 @@ void Menu_view::Dialog::_handle_input()
|
|||||||
_action.hover_changed();
|
_action.hover_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Menu_view::Dialog::_handle_gui_sync()
|
||||||
|
{
|
||||||
|
Ticks const now = _action.now();
|
||||||
|
|
||||||
|
Ticks const passed_ticks { now.cs - _previous_sync.cs };
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < passed_ticks.cs; i++)
|
||||||
|
_animate();
|
||||||
|
|
||||||
|
if (passed_ticks.cs)
|
||||||
|
_redraw();
|
||||||
|
|
||||||
|
/* deactivate sync signalling when idle */
|
||||||
|
if (_gui_sync_enabled && !_local_animator.active()) {
|
||||||
|
_gui.framebuffer.sync_sigh(Signal_context_capability());
|
||||||
|
_gui_sync_enabled = false;
|
||||||
|
}
|
||||||
|
_previous_sync = now;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _DIALOG_H_ */
|
#endif /* _DIALOG_H_ */
|
||||||
|
@ -89,7 +89,7 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
|
|||||||
_hover = node.attribute_value("hover", false);
|
_hover = node.attribute_value("hover", false);
|
||||||
|
|
||||||
_factory.styles.with_label_style(node, [&] (Label_style style) {
|
_factory.styles.with_label_style(node, [&] (Label_style style) {
|
||||||
_color.fade_to(style.color, Animated_color::Steps{80}); });
|
_color.fade_to(style.color, Animated_color::Steps{40}); });
|
||||||
|
|
||||||
if (node.has_attribute("text")) {
|
if (node.has_attribute("text")) {
|
||||||
_text = node.attribute_value("text", _text);
|
_text = node.attribute_value("text", _text);
|
||||||
|
@ -83,45 +83,14 @@ struct Menu_view::Main : Dialog::Action
|
|||||||
|
|
||||||
} _input_seq_number { };
|
} _input_seq_number { };
|
||||||
|
|
||||||
struct Frame { uint64_t count; };
|
Timer::Connection _timer { _env };
|
||||||
|
|
||||||
/*
|
|
||||||
* Timer used for animating widgets
|
|
||||||
*/
|
|
||||||
struct Frame_timer : Timer::Connection
|
|
||||||
{
|
|
||||||
enum { PERIOD = 10 };
|
|
||||||
|
|
||||||
Frame curr_frame() const { return { elapsed_ms() / PERIOD }; }
|
|
||||||
|
|
||||||
void schedule() { trigger_once((Genode::uint64_t)Frame_timer::PERIOD*1000); }
|
|
||||||
|
|
||||||
Frame_timer(Env &env) : Timer::Connection(env) { }
|
|
||||||
|
|
||||||
} _timer { _env };
|
|
||||||
|
|
||||||
void _handle_frame_timer();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog::Action
|
* Dialog::Action
|
||||||
*/
|
*/
|
||||||
void trigger_redraw() override
|
Ticks now() override
|
||||||
{
|
{
|
||||||
/*
|
return { .cs = _timer.curr_time().trunc_to_plain_ms().value / 10 };
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,38 +106,16 @@ struct Menu_view::Main : Dialog::Action
|
|||||||
_input_seq_number.update(seq);
|
_input_seq_number.update(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
Signal_handler<Main> _frame_timer_handler = {
|
|
||||||
_env.ep(), *this, &Main::_handle_frame_timer};
|
|
||||||
|
|
||||||
Constructible<Genode::Expanding_reporter> _hover_reporter { };
|
Constructible<Genode::Expanding_reporter> _hover_reporter { };
|
||||||
|
|
||||||
void _update_hover_report();
|
void _update_hover_report();
|
||||||
|
|
||||||
/**
|
|
||||||
* Frame of last call of 'handle_frame_timer'
|
|
||||||
*/
|
|
||||||
Frame _last_frame { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of frames between two redraws
|
|
||||||
*/
|
|
||||||
static constexpr unsigned REDRAW_PERIOD = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counter used for triggering redraws. Incremented in each frame-timer
|
|
||||||
* period, wraps at 'REDRAW_PERIOD'. The redraw is performed when the
|
|
||||||
* counter wraps.
|
|
||||||
*/
|
|
||||||
unsigned _frame_cnt = 0;
|
|
||||||
|
|
||||||
Main(Env &env, Vfs::Env &libc_vfs_env)
|
Main(Env &env, Vfs::Env &libc_vfs_env)
|
||||||
:
|
:
|
||||||
_env(env), _vfs_env(libc_vfs_env)
|
_env(env), _vfs_env(libc_vfs_env)
|
||||||
{
|
{
|
||||||
_config.sigh(_config_handler);
|
_config.sigh(_config_handler);
|
||||||
_config_handler.local_submit(); /* apply initial configuration */
|
_config_handler.local_submit(); /* apply initial configuration */
|
||||||
|
|
||||||
_timer.sigh(_frame_timer_handler);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -239,57 +186,10 @@ void Menu_view::Main::_handle_config()
|
|||||||
/* re-assign font pointers in labels (needed due to font style change) */
|
/* re-assign font pointers in labels (needed due to font style change) */
|
||||||
if (!_styles.up_to_date()) {
|
if (!_styles.up_to_date()) {
|
||||||
_dialogs.for_each([&] (Dialog &dialog) {
|
_dialogs.for_each([&] (Dialog &dialog) {
|
||||||
dialog._handle_dialog();
|
dialog.enforce_font_sytle_change(); });
|
||||||
|
|
||||||
/* fast-forward geometry animation on font changes */
|
|
||||||
while (dialog.animation_in_progress())
|
|
||||||
dialog.animate();
|
|
||||||
});
|
|
||||||
|
|
||||||
_styles.flush_outdated_styles();
|
_styles.flush_outdated_styles();
|
||||||
}
|
}
|
||||||
trigger_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Menu_view::Main::_handle_frame_timer()
|
|
||||||
{
|
|
||||||
_frame_cnt++;
|
|
||||||
|
|
||||||
Frame const curr_frame = _timer.curr_frame();
|
|
||||||
|
|
||||||
unsigned const passed_frames =
|
|
||||||
max(unsigned(curr_frame.count - _last_frame.count), 4U);
|
|
||||||
|
|
||||||
if (passed_frames > 0)
|
|
||||||
_dialogs.for_each([&] (Dialog &dialog) {
|
|
||||||
for (unsigned i = 0; i < passed_frames; i++)
|
|
||||||
dialog.animate(); });
|
|
||||||
|
|
||||||
bool any_redraw_scheduled = false;
|
|
||||||
_dialogs.for_each([&] (Dialog const &dialog) {
|
|
||||||
any_redraw_scheduled |= dialog.redraw_scheduled(); });
|
|
||||||
|
|
||||||
_last_frame = curr_frame;
|
|
||||||
|
|
||||||
bool const redraw_skipped = any_redraw_scheduled && (_frame_cnt < REDRAW_PERIOD);
|
|
||||||
|
|
||||||
if (!redraw_skipped) {
|
|
||||||
_frame_cnt = 0;
|
|
||||||
_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.
|
|
||||||
*/
|
|
||||||
if (any_animation_in_progress || redraw_skipped)
|
|
||||||
_timer.schedule();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,6 +36,8 @@ namespace Menu_view {
|
|||||||
using Point = Surface_base::Point;
|
using Point = Surface_base::Point;
|
||||||
using Area = Surface_base::Area;
|
using Area = Surface_base::Area;
|
||||||
using Rect = Surface_base::Rect;
|
using Rect = Surface_base::Rect;
|
||||||
|
|
||||||
|
struct Ticks { uint64_t cs; /* centi-seconds (10 ms) */ };
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _TYPES_H_ */
|
#endif /* _TYPES_H_ */
|
||||||
|
@ -110,7 +110,7 @@ class Menu_view::Widget : List_model<Widget>::Element
|
|||||||
return node.attribute_value("version", Version());
|
return node.attribute_value("version", Version());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Animated_rect::Steps motion_steps() { return { 60 }; };
|
static Animated_rect::Steps motion_steps() { return { 30 }; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user