mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 08:25:38 +00:00
menu_view: animate geometric changes
This commit is contained in:
parent
bb52181bc5
commit
063413ca1f
96
repos/gems/src/app/menu_view/animated_geometry.h
Normal file
96
repos/gems/src/app/menu_view/animated_geometry.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* \brief Helper for implementing geometric transitions
|
||||
* \author Norman Feske
|
||||
* \date 2017-08-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 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 _ANIMATED_GEOMETRY_H_
|
||||
#define _ANIMATED_GEOMETRY_H_
|
||||
|
||||
/* demo includes */
|
||||
#include <util/lazy_value.h>
|
||||
#include <gems/animator.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
|
||||
namespace Menu_view { class Animated_rect; }
|
||||
|
||||
|
||||
class Menu_view::Animated_rect : public Rect, Animator::Item, Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
struct Steps { unsigned value; };
|
||||
|
||||
private:
|
||||
|
||||
struct Animated_point
|
||||
{
|
||||
bool _initial = true;
|
||||
|
||||
Lazy_value<int> _x, _y;
|
||||
|
||||
void animate() { _x.animate(); _y.animate(); }
|
||||
|
||||
bool animated() const { return _x != _x.dst() || _y != _y.dst(); }
|
||||
|
||||
void move_to(Point p, Steps steps)
|
||||
{
|
||||
if (_initial) {
|
||||
_x = Lazy_value<int>(p.x() << 10);
|
||||
_y = Lazy_value<int>(p.y() << 10);
|
||||
_initial = false;
|
||||
} else {
|
||||
_x.dst(p.x() << 10, steps.value);
|
||||
_y.dst(p.y() << 10, steps.value);
|
||||
}
|
||||
}
|
||||
|
||||
int x() const { return _x >> 10; }
|
||||
int y() const { return _y >> 10; }
|
||||
};
|
||||
|
||||
Animated_point _p1, _p2;
|
||||
|
||||
public:
|
||||
|
||||
Animated_rect(Animator &animator) : Animator::Item(animator) { }
|
||||
|
||||
/**
|
||||
* Animator::Item interface
|
||||
*/
|
||||
void animate() override
|
||||
{
|
||||
_p1.animate(); _p2.animate();
|
||||
|
||||
static_cast<Rect &>(*this) = Rect(Point(_p1.x(), _p1.y()),
|
||||
Point(_p2.x(), _p2.y()));
|
||||
|
||||
/* schedule / de-schedule animation */
|
||||
animated(_p1.animated() || _p2.animated());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign new target coordinates
|
||||
*
|
||||
* The first assignment moves the rectangle directly to the target
|
||||
* position without animation. All subsequent assignments result in an
|
||||
* animated movement.
|
||||
*/
|
||||
void move_to(Rect rect, Steps steps)
|
||||
{
|
||||
_p1.move_to(rect.p1(), steps);
|
||||
_p2.move_to(rect.p2(), steps);
|
||||
animate();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _ANIMATED_GEOMETRY_H_ */
|
@ -56,18 +56,18 @@ struct Menu_view::Box_layout_widget : Widget
|
||||
Area const child_min_size = w->min_size();
|
||||
|
||||
if (_direction == VERTICAL) {
|
||||
w->geometry = Rect(position, Area(largest_size, child_min_size.h()));
|
||||
w->geometry(Rect(position, Area(largest_size, child_min_size.h())));
|
||||
unsigned const next_top_margin = w->next() ? w->next()->margin.top : 0;
|
||||
unsigned const dy = child_min_size.h() - min(w->margin.bottom, next_top_margin);
|
||||
position = position + Point(0, dy);
|
||||
} else {
|
||||
w->geometry = Rect(position, Area(child_min_size.w(), largest_size));
|
||||
w->geometry(Rect(position, Area(child_min_size.w(), largest_size)));
|
||||
unsigned const next_left_margin = w->next() ? w->next()->margin.left : 0;
|
||||
unsigned const dx = child_min_size.w() - min(w->margin.right, next_left_margin);
|
||||
position = position + Point(dx, 0);
|
||||
}
|
||||
|
||||
_min_size = Area(w->geometry.x2() + 1, w->geometry.y2() + 1);
|
||||
_min_size = Area(w->geometry().x2() + 1, w->geometry().y2() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +87,9 @@ struct Menu_view::Box_layout_widget : Widget
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
||||
if (_direction == VERTICAL)
|
||||
w->size(Area(geometry.w(), w->min_size().h()));
|
||||
w->size(Area(geometry().w(), w->min_size().h()));
|
||||
else
|
||||
w->size(Area(w->min_size().w(), geometry.h()));
|
||||
w->size(Area(w->min_size().w(), geometry().h()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -86,9 +86,9 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
||||
bool const dy = selected ? 1 : 0;
|
||||
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry = Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top + dy),
|
||||
child->min_size());
|
||||
child->geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top + dy),
|
||||
child->min_size()));
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
@ -130,10 +130,10 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
||||
/*
|
||||
* Apply blended texture to target surface
|
||||
*/
|
||||
Icon_painter::paint(pixel_surface, Rect(at, geometry.area()),
|
||||
Icon_painter::paint(pixel_surface, Rect(at, _animated_geometry.area()),
|
||||
scratch.texture(), 255);
|
||||
|
||||
Icon_painter::paint(alpha_surface, Rect(at, geometry.area()),
|
||||
Icon_painter::paint(alpha_surface, Rect(at, _animated_geometry.area()),
|
||||
scratch.texture(), 255);
|
||||
|
||||
_draw_children(pixel_surface, alpha_surface, at);
|
||||
@ -142,8 +142,8 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
||||
void _layout() override
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next())
|
||||
w->size(Area(geometry.w() - _space().w(),
|
||||
geometry.h() - _space().h()));
|
||||
w->size(Area(geometry().w() - _space().w(),
|
||||
geometry().h() - _space().h()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,18 +289,18 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
|
||||
int centered_breadth_pos(Depth_direction dir) const
|
||||
{
|
||||
return dir.horizontal() ? (_widget.geometry.y1() + _widget.geometry.y2()) / 2
|
||||
: (_widget.geometry.x1() + _widget.geometry.x2()) / 2;
|
||||
return dir.horizontal() ? (_widget.geometry().y1() + _widget.geometry().y2()) / 2
|
||||
: (_widget.geometry().x1() + _widget.geometry().x2()) / 2;
|
||||
}
|
||||
|
||||
unsigned _edge_size(Depth_direction dir) const
|
||||
{
|
||||
if (dir.horizontal())
|
||||
return max(0, (int)_widget.geometry.h() - (int)_widget.margin.top
|
||||
- (int)_widget.margin.bottom);
|
||||
return max(0, (int)_widget.geometry().h() - (int)_widget.margin.top
|
||||
- (int)_widget.margin.bottom);
|
||||
else
|
||||
return max(0, (int)_widget.geometry.w() - (int)_widget.margin.left
|
||||
- (int)_widget.margin.right);
|
||||
return max(0, (int)_widget.geometry().w() - (int)_widget.margin.left
|
||||
- (int)_widget.margin.right);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,7 +496,7 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
if (client && !server) {
|
||||
warning("node '", client_name, "' depends on "
|
||||
"non-existing node '", server_name, "'");
|
||||
client->_widget.geometry = Rect(Point(0, 0), Area(0, 0));
|
||||
client->_widget.geometry(Rect(Point(0, 0), Area(0, 0)));
|
||||
}
|
||||
});
|
||||
|
||||
@ -548,9 +548,9 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
: Rect(Point(breadth_pos, depth_pos),
|
||||
Area(breadth_size, depth_size));
|
||||
|
||||
w->geometry = Rect(node_rect.center(w->min_size()), w->min_size());
|
||||
w->geometry(Rect(node_rect.center(w->min_size()), w->min_size()));
|
||||
|
||||
bounding_box = Rect::compound(bounding_box, w->geometry);
|
||||
bounding_box = Rect::compound(bounding_box, w->geometry());
|
||||
});
|
||||
}
|
||||
|
||||
@ -562,15 +562,15 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
|
||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
||||
|
||||
int x = w->geometry.x1(), y = w->geometry.y1();
|
||||
int x = w->geometry().x1(), y = w->geometry().y1();
|
||||
|
||||
if (_depth_direction.value == Depth_direction::NORTH)
|
||||
y = (int)bounding_box.h() - y - w->geometry.h();
|
||||
y = (int)bounding_box.h() - y - w->geometry().h();
|
||||
|
||||
if (_depth_direction.value == Depth_direction::WEST)
|
||||
x = (int)bounding_box.w() - x - w->geometry.w();
|
||||
x = (int)bounding_box.w() - x - w->geometry().w();
|
||||
|
||||
w->geometry = Rect(Point(x, y), w->geometry.area());
|
||||
w->geometry(Rect(Point(x, y), w->geometry().area()));
|
||||
}
|
||||
}
|
||||
_min_size = bounding_box.area();
|
||||
@ -658,7 +658,7 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
void _layout() override
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next())
|
||||
w->size(w->geometry.area());
|
||||
w->size(w->geometry().area());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -32,18 +32,18 @@ struct Menu_view::Float_widget : Widget
|
||||
Area const child_min = child.min_size();
|
||||
|
||||
/* space around the minimal-sized child */
|
||||
int const w_space = geometry.w() - child_min.w();
|
||||
int const h_space = geometry.h() - child_min.h();
|
||||
int const w_space = geometry().w() - child_min.w();
|
||||
int const h_space = geometry().h() - child_min.h();
|
||||
|
||||
/* stretch child size opposite attributes are specified */
|
||||
int const w = (_east && _west) ? geometry.w() : child_min.w();
|
||||
int const h = (_north && _south) ? geometry.h() : child_min.h();
|
||||
int const w = (_east && _west) ? geometry().w() : child_min.w();
|
||||
int const h = (_north && _south) ? geometry().h() : child_min.h();
|
||||
|
||||
/* align / center child position */
|
||||
int const x = _east ? 0 : _west ? w_space : w_space / 2;
|
||||
int const y = _north ? 0 : _south ? h_space : h_space / 2;
|
||||
|
||||
child.geometry = Rect(Point(x, y), Area(w, h));
|
||||
child.geometry(Rect(Point(x, y), Area(w, h)));
|
||||
}
|
||||
|
||||
void update(Xml_node node) override
|
||||
@ -77,7 +77,7 @@ struct Menu_view::Float_widget : Widget
|
||||
{
|
||||
if (Widget *child = _children.first()) {
|
||||
_place_child(*child);
|
||||
child->size(child->geometry.area());
|
||||
child->size(child->geometry().area());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -48,9 +48,9 @@ struct Menu_view::Frame_widget : Widget
|
||||
* layout
|
||||
*/
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry = Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top),
|
||||
child->min_size());
|
||||
child->geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top),
|
||||
child->min_size()));
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
@ -70,10 +70,10 @@ struct Menu_view::Frame_widget : Widget
|
||||
Surface<Pixel_alpha8> &alpha_surface,
|
||||
Point at) const
|
||||
{
|
||||
Icon_painter::paint(pixel_surface, Rect(at, geometry.area()),
|
||||
Icon_painter::paint(pixel_surface, Rect(at, _animated_geometry.area()),
|
||||
*texture, 255);
|
||||
|
||||
Icon_painter::paint(alpha_surface, Rect(at, geometry.area()),
|
||||
Icon_painter::paint(alpha_surface, Rect(at, _animated_geometry.area()),
|
||||
*texture, 255);
|
||||
|
||||
_draw_children(pixel_surface, alpha_surface, at);
|
||||
@ -82,8 +82,8 @@ struct Menu_view::Frame_widget : Widget
|
||||
void _layout() override
|
||||
{
|
||||
if (Widget *child = _children.first())
|
||||
child->size(Area(geometry.w() - _space().w(),
|
||||
geometry.h() - _space().h()));
|
||||
child->size(Area(geometry().w() - _space().w(),
|
||||
geometry().h() - _space().h()));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -56,8 +56,8 @@ struct Menu_view::Label_widget : Widget
|
||||
|
||||
Area text_size = min_size();
|
||||
|
||||
int const dx = (int)geometry.w() - text_size.w(),
|
||||
dy = (int)geometry.h() - text_size.h();
|
||||
int const dx = (int)geometry().w() - text_size.w(),
|
||||
dy = (int)geometry().h() - text_size.h();
|
||||
|
||||
Point const centered = Point(dx/2, dy/2);
|
||||
|
||||
|
@ -27,6 +27,14 @@ struct Menu_view::Root_widget : Widget
|
||||
Widget(factory, node, unique_id)
|
||||
{ }
|
||||
|
||||
Area animated_size() const
|
||||
{
|
||||
if (Widget const * const child = _children.first())
|
||||
return child->animated_geometry().area();
|
||||
|
||||
return Area(1, 1);
|
||||
}
|
||||
|
||||
void update(Xml_node node) override
|
||||
{
|
||||
char const *dialog_tag = "dialog";
|
||||
@ -42,6 +50,9 @@ struct Menu_view::Root_widget : Widget
|
||||
}
|
||||
|
||||
_update_children(node);
|
||||
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry(Rect(Point(0, 0), child->min_size()));
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
@ -62,7 +73,7 @@ struct Menu_view::Root_widget : Widget
|
||||
void _layout() override
|
||||
{
|
||||
if (Widget *child = _children.first()) {
|
||||
child->size(geometry.area());
|
||||
child->size(geometry().area());
|
||||
child->position(Point(0, 0));
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
/* local includes */
|
||||
#include <widget_factory.h>
|
||||
#include <list_model_from_xml.h>
|
||||
#include <animated_geometry.h>
|
||||
|
||||
namespace Menu_view {
|
||||
|
||||
@ -136,7 +137,7 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
Point at) const
|
||||
{
|
||||
for (Widget const *w = _children.first(); w; w = w->next())
|
||||
w->draw(pixel_surface, alpha_surface, at + w->geometry.p1());
|
||||
w->draw(pixel_surface, alpha_surface, at + w->_animated_geometry.p1());
|
||||
}
|
||||
|
||||
virtual void _layout() { }
|
||||
@ -144,28 +145,42 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
Rect _inner_geometry() const
|
||||
{
|
||||
return Rect(Point(margin.left, margin.top),
|
||||
Area(geometry.w() - margin.horizontal(),
|
||||
geometry.h() - margin.vertical()));
|
||||
Area(geometry().w() - margin.horizontal(),
|
||||
geometry().h() - margin.vertical()));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Margin margin { 0, 0, 0, 0 };
|
||||
|
||||
/*
|
||||
* Position relative to the parent widget and actual size, defined by
|
||||
* the parent
|
||||
*/
|
||||
Rect geometry;
|
||||
Rect _geometry;
|
||||
|
||||
Animated_rect _animated_geometry { _factory.animator };
|
||||
|
||||
public:
|
||||
|
||||
Margin margin { 0, 0, 0, 0 };
|
||||
|
||||
void geometry(Rect geometry)
|
||||
{
|
||||
_geometry = geometry;
|
||||
_animated_geometry.move_to(_geometry, Animated_rect::Steps{60});
|
||||
}
|
||||
|
||||
Rect geometry() const { return _geometry; }
|
||||
|
||||
Rect animated_geometry() const { return _animated_geometry; }
|
||||
|
||||
/*
|
||||
* Return x/y positions of the edges of the widget with the margin
|
||||
* applied
|
||||
*/
|
||||
Rect edges() const { return Rect(Point(geometry.x1() + margin.left,
|
||||
geometry.y1() + margin.top),
|
||||
Point(geometry.x2() - margin.right,
|
||||
geometry.y2() - margin.bottom)); }
|
||||
Rect edges() const
|
||||
{
|
||||
Rect const r = _animated_geometry;
|
||||
return Rect(Point(r.x1() + margin.left, r.y1() + margin.top),
|
||||
Point(r.x2() - margin.right, r.y2() - margin.bottom));
|
||||
}
|
||||
|
||||
Widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
|
||||
:
|
||||
@ -195,14 +210,14 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
|
||||
void size(Area size)
|
||||
{
|
||||
geometry = Rect(geometry.p1(), size);
|
||||
_geometry = Rect(_geometry.p1(), size);
|
||||
|
||||
_layout();
|
||||
}
|
||||
|
||||
void position(Point position)
|
||||
{
|
||||
geometry = Rect(position, geometry.area());
|
||||
_geometry = Rect(position, _geometry.area());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,7 +231,7 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
return Unique_id();
|
||||
|
||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
||||
Unique_id res = w->hovered(at - w->geometry.p1());
|
||||
Unique_id res = w->hovered(at - w->geometry().p1());
|
||||
if (res.valid())
|
||||
return res;
|
||||
}
|
||||
@ -236,13 +251,13 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
xml.node(_type_name.string(), [&]() {
|
||||
|
||||
xml.attribute("name", _name.string());
|
||||
xml.attribute("xpos", geometry.x1());
|
||||
xml.attribute("ypos", geometry.y1());
|
||||
xml.attribute("width", geometry.w());
|
||||
xml.attribute("height", geometry.h());
|
||||
xml.attribute("xpos", geometry().x1());
|
||||
xml.attribute("ypos", geometry().y1());
|
||||
xml.attribute("width", geometry().w());
|
||||
xml.attribute("height", geometry().h());
|
||||
|
||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
||||
w->gen_hover_model(xml, at - w->geometry.p1());
|
||||
w->gen_hover_model(xml, at - w->geometry().p1());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user