menu_view: animate geometric changes

This commit is contained in:
Norman Feske 2017-08-17 17:11:38 +02:00 committed by Christian Helmuth
parent bb52181bc5
commit 063413ca1f
9 changed files with 184 additions and 62 deletions

View 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_ */

View File

@ -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()));
}
}
};

View File

@ -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()));
}

View File

@ -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());
}
};

View File

@ -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());
}
}
};

View File

@ -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()));
}
};

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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());
}
});
}