mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-09 04:15:52 +00:00
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:
parent
38aef49428
commit
1713583a19
117
repos/gems/src/app/menu_view/animated_color.h
Normal file
117
repos/gems/src/app/menu_view/animated_color.h
Normal 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_ */
|
@ -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()); });
|
||||
|
@ -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 { };
|
||||
|
||||
|
@ -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_ */
|
||||
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
<style color="#00000000"/>
|
Loading…
x
Reference in New Issue
Block a user