mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-22 15:02: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
|
||||
* 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;
|
||||
if (_hovered && !new_hovered) steps = MEDIUM;
|
||||
if (!_hovered && new_hovered) steps = FAST;
|
||||
|
@ -122,7 +122,7 @@ class Menu_view::Cursor : List_model<Cursor>::Element
|
||||
|
||||
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
|
||||
{
|
||||
virtual void trigger_redraw() = 0;
|
||||
virtual void hover_changed() = 0;
|
||||
virtual void observed_seq_number(Input::Seq_number) = 0;
|
||||
virtual Ticks now() = 0;
|
||||
};
|
||||
|
||||
Action &_action;
|
||||
@ -72,6 +72,15 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
||||
|
||||
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 { };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void redraw()
|
||||
void _redraw()
|
||||
{
|
||||
if (!_redraw_scheduled)
|
||||
return;
|
||||
@ -190,7 +199,7 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
||||
|
||||
bool hovered() const { return _hovered; }
|
||||
|
||||
void animate()
|
||||
void _animate()
|
||||
{
|
||||
bool const progress = _local_animator.active();
|
||||
|
||||
@ -200,9 +209,14 @@ struct Menu_view::Dialog : List_model<Dialog>::Element
|
||||
_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
|
||||
@ -237,7 +251,13 @@ void Menu_view::Dialog::_handle_dialog()
|
||||
_redraw_scheduled = true;
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
|
||||
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_ */
|
||||
|
@ -89,7 +89,7 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
|
||||
_hover = node.attribute_value("hover", false);
|
||||
|
||||
_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")) {
|
||||
_text = node.attribute_value("text", _text);
|
||||
|
@ -83,45 +83,14 @@ struct Menu_view::Main : Dialog::Action
|
||||
|
||||
} _input_seq_number { };
|
||||
|
||||
struct Frame { uint64_t count; };
|
||||
|
||||
/*
|
||||
* 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();
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
/**
|
||||
* Dialog::Action
|
||||
*/
|
||||
void trigger_redraw() override
|
||||
Ticks now() 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();
|
||||
}
|
||||
return { .cs = _timer.curr_time().trunc_to_plain_ms().value / 10 };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,38 +106,16 @@ struct Menu_view::Main : Dialog::Action
|
||||
_input_seq_number.update(seq);
|
||||
}
|
||||
|
||||
Signal_handler<Main> _frame_timer_handler = {
|
||||
_env.ep(), *this, &Main::_handle_frame_timer};
|
||||
|
||||
Constructible<Genode::Expanding_reporter> _hover_reporter { };
|
||||
|
||||
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)
|
||||
:
|
||||
_env(env), _vfs_env(libc_vfs_env)
|
||||
{
|
||||
_config.sigh(_config_handler);
|
||||
_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) */
|
||||
if (!_styles.up_to_date()) {
|
||||
_dialogs.for_each([&] (Dialog &dialog) {
|
||||
dialog._handle_dialog();
|
||||
|
||||
/* fast-forward geometry animation on font changes */
|
||||
while (dialog.animation_in_progress())
|
||||
dialog.animate();
|
||||
});
|
||||
dialog.enforce_font_sytle_change(); });
|
||||
|
||||
_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 Area = Surface_base::Area;
|
||||
using Rect = Surface_base::Rect;
|
||||
|
||||
struct Ticks { uint64_t cs; /* centi-seconds (10 ms) */ };
|
||||
}
|
||||
|
||||
#endif /* _TYPES_H_ */
|
||||
|
@ -110,7 +110,7 @@ class Menu_view::Widget : List_model<Widget>::Element
|
||||
return node.attribute_value("version", Version());
|
||||
}
|
||||
|
||||
static Animated_rect::Steps motion_steps() { return { 60 }; };
|
||||
static Animated_rect::Steps motion_steps() { return { 30 }; };
|
||||
|
||||
protected:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user