menu_view: basic support for styling labels

This patch allows for the customization of the text color and alpha
value of the label widget by the means of a style-definition file.
The mechanism is exemplified with the new "invisible" label style
that sets the alpha value to zero.

Issue #3629
This commit is contained in:
Norman Feske 2020-02-19 14:44:53 +01:00 committed by Christian Helmuth
parent 38aef49428
commit 1713583a19
6 changed files with 227 additions and 21 deletions

View File

@ -0,0 +1,117 @@
/*
* \brief Helper for implementing the fading of colors
* \author Norman Feske
* \date 2020-02-19
*/
/*
* Copyright (C) 2020 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_COLOR_H_
#define _ANIMATED_COLOR_H_
/* demo includes */
#include <util/lazy_value.h>
#include <util/color.h>
#include <gems/animator.h>
namespace Genode { class Animated_color; }
class Genode::Animated_color : private Animator::Item, Noncopyable
{
public:
struct Steps { unsigned value; };
private:
Color _color { };
using Lazy_value = ::Lazy_value<int>;
struct Animated_channel
{
bool _initial = true;
Lazy_value _value { };
Steps _remaining { 0 };
void animate()
{
_value.animate();
if (_remaining.value > 1)
_remaining.value--;
}
bool animated() const { return _value != _value.dst(); }
void fade_to(int value, Steps steps)
{
if (_initial) {
_value = Lazy_value(value << 10);
_initial = false;
} else {
/* retarget animation while already in progress */
if (animated())
steps.value = max(_remaining.value, 1U);
_value.dst(value << 10, steps.value);
_remaining = steps;
}
}
int value() const { return _value >> 10; }
};
Animated_channel _r { }, _g { }, _b { }, _a { };
public:
Animated_color(Animator &animator) : Animator::Item(animator) { }
/**
* Animator::Item interface
*/
void animate() override
{
_r.animate(); _g.animate(); _b.animate(); _a.animate();
_color = Color(_r.value(), _g.value(), _b.value(), _a.value());
/* schedule / de-schedule animation */
Animator::Item::animated(_r.animated() || _g.animated() ||
_b.animated() || _a.animated());
}
/**
* Assign new target color
*
* The first assignment assigns the color directly without animation.
* All subsequent assignments result in an animated transition to the
* target color.
*/
void fade_to(Color color, Steps steps)
{
_r.fade_to(color.r, steps);
_g.fade_to(color.g, steps);
_b.fade_to(color.b, steps);
_a.fade_to(color.a, steps);
animate();
}
bool animated() const { return Animator::Item::animated(); }
Color color() const { return _color; }
};
#endif /* _ANIMATED_COLOR_H_ */

View File

@ -17,6 +17,7 @@
/* local includes */
#include <widget_factory.h>
#include <text_selection.h>
#include <animated_color.h>
namespace Menu_view { struct Label_widget; }
@ -29,6 +30,8 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
typedef String<200> Text;
Text _text { };
Animated_color _color;
int _min_width = 0;
int _min_height = 0;
@ -41,6 +44,7 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
Label_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
:
Widget(factory, node, unique_id),
_color(factory.animator),
_cursor_update_policy(factory, *this),
_selection_update_policy(factory.alloc, *this)
{ }
@ -58,6 +62,9 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
_min_width = 0;
_min_height = 0;
_factory.styles.with_label_style(node, [&] (Label_style style) {
_color.fade_to(style.color, Animated_color::Steps{80}); });
if (node.has_attribute("text")) {
_text = node.attribute_value("text", _text);
_text = Xml_unquoted(_text);
@ -100,13 +107,18 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
_selections.for_each([&] (Text_selection const &selection) {
selection.draw(pixel_surface, alpha_surface, at, text_size.h()); });
Text_painter::paint(pixel_surface,
Text_painter::Position(centered.x(), centered.y()),
*_font, Color(0, 0, 0), _text.string());
Color const color = _color.color();
int const alpha = color.a;
Text_painter::paint(alpha_surface,
Text_painter::Position(centered.x(), centered.y()),
*_font, Color(255, 255, 255), _text.string());
if (alpha) {
Text_painter::paint(pixel_surface,
Text_painter::Position(centered.x(), centered.y()),
*_font, color, _text.string());
Text_painter::paint(alpha_surface,
Text_painter::Position(centered.x(), centered.y()),
*_font, Color(alpha, alpha, alpha, alpha), _text.string());
}
_cursors.for_each([&] (Cursor const &cursor) {
cursor.draw(pixel_surface, alpha_surface, at, text_size.h()); });

View File

@ -111,10 +111,11 @@ struct Menu_view::Main
} _vfs_env;
Directory _root_dir { _vfs_env };
Directory _fonts_dir { _root_dir, "fonts" };
Directory _root_dir { _vfs_env };
Directory _fonts_dir { _root_dir, "fonts" };
Directory _styles_dir { _root_dir, "styles" };
Style_database _styles { _env.ram(), _env.rm(), _heap, _fonts_dir };
Style_database _styles { _env.ram(), _env.rm(), _heap, _fonts_dir, _styles_dir };
Animator _animator { };

View File

@ -23,7 +23,17 @@
/* local includes */
#include "types.h"
namespace Menu_view { struct Style_database; }
namespace Menu_view {
struct Label_style;
struct Style_database;
}
struct Menu_view::Label_style
{
Color color;
};
class Menu_view::Style_database
@ -36,6 +46,35 @@ class Menu_view::Style_database
typedef ::File::Reading_failed Reading_failed;
struct Label_style_entry : List<Label_style_entry>::Element, Noncopyable
{
Path const path; /* needed for lookup */
Label_style const style;
static Label_style _init_style(Allocator &alloc,
Directory const &styles_dir,
Path const &path)
{
Label_style result { .color = Color(0, 0, 0) };
try {
File_content const content(alloc, styles_dir, path,
File_content::Limit{1024});
content.xml([&] (Xml_node node) {
result.color = node.attribute_value("color", result.color);
});
} catch (...) { }
return result;
}
Label_style_entry(Allocator &alloc, Directory const &styles_dir,
Path const &path)
:
path(path), style(_init_style(alloc, styles_dir, path))
{ }
};
struct Texture_entry : List<Texture_entry>::Element
{
Path const path;
@ -86,13 +125,15 @@ class Menu_view::Style_database
Region_map &_rm;
Allocator &_alloc;
Directory const &_fonts_dir;
Directory const &_styles_dir;
/*
* The list is mutable because it is populated as a side effect of
* calling the const lookup function.
* The lists are mutable because they are populated as a side effect of
* calling the const lookup functions.
*/
List<Texture_entry> mutable _textures { };
List<Font_entry> mutable _fonts { };
List<Texture_entry> mutable _textures { };
List<Font_entry> mutable _fonts { };
List<Label_style_entry> mutable _label_styles { };
template <typename T>
T const *_lookup(List<T> &list, char const *path) const
@ -105,28 +146,56 @@ class Menu_view::Style_database
}
/*
* Assemble path name 'styles/<widget>/<style>/<name>.<extension>'
* Assemble path name 'styles/<widget>/<style>/<name>.png'
*/
static Path _construct_path(Xml_node node, char const *name,
char const *extension)
static Path _construct_png_path(Xml_node node, char const *name)
{
typedef String<64> Style;
Style const style = node.attribute_value("style", Style("default"));
return Path("/styles/", node.type(), "/", style, "/", name, ".", extension);
return Path("/styles/", node.type(), "/", style, "/", name, ".png");
}
/*
* Assemble path of style file relative to the styles directory
*/
static Path _widget_style_path(Xml_node const &node)
{
typedef String<64> Style;
Style const style = node.attribute_value("style", Style("default"));
return Path(node.type(), "/", style, "/", "style");
}
Label_style const &_label_style(Xml_node node) const
{
Path const path = _widget_style_path(node);
if (Label_style_entry const *e = _lookup(_label_styles, path.string()))
return e->style;
/*
* Load and remember style
*/
Label_style_entry &e = *new (_alloc)
Label_style_entry(_alloc, _styles_dir, path);
_label_styles.insert(&e);
return e.style;
}
public:
Style_database(Ram_allocator &ram, Region_map &rm, Allocator &alloc,
Directory const &fonts_dir)
Directory const &fonts_dir, Directory const &styles_dir)
:
_ram(ram), _rm(rm), _alloc(alloc), _fonts_dir(fonts_dir)
_ram(ram), _rm(rm), _alloc(alloc),
_fonts_dir(fonts_dir), _styles_dir(styles_dir)
{ }
Texture<Pixel_rgb888> const *texture(Xml_node node, char const *png_name) const
{
Path const path = _construct_path(node, png_name, "png");
Path const path = _construct_png_path(node, png_name);
if (Texture_entry const *e = _lookup(_textures, path.string()))
return &e->texture;
@ -173,6 +242,12 @@ class Menu_view::Style_database
return nullptr;
}
template <typename FN>
void with_label_style(Xml_node node, FN const &fn) const
{
fn(_label_style(node));
}
};
#endif /* _STYLE_DATABASE_H_ */

View File

@ -0,0 +1 @@
<style color="#00000000"/>