mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
Window decorator that can be styled
This commit is contained in:
parent
2dde77f62c
commit
4b9e1f1060
53
repos/gems/src/app/themed_decorator/config.h
Normal file
53
repos/gems/src/app/themed_decorator/config.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* \brief Decorator configuration handling
|
||||
* \author Norman Feske
|
||||
* \date 2015-09-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/session_policy.h>
|
||||
#include <util/color.h>
|
||||
|
||||
/* decorator includes */
|
||||
#include <decorator/types.h>
|
||||
|
||||
namespace Decorator {
|
||||
|
||||
class Config;
|
||||
|
||||
typedef Genode::String<200> Window_title;
|
||||
}
|
||||
|
||||
|
||||
class Decorator::Config
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Return the base color of the window with the specified title
|
||||
*/
|
||||
Color base_color(Window_title const &title) const
|
||||
{
|
||||
Color result(0, 0, 0);
|
||||
|
||||
try {
|
||||
Genode::Session_policy policy(title);
|
||||
result = policy.attribute_value("color", result);
|
||||
|
||||
} catch (Genode::Session_policy::No_policy_defined) { }
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
304
repos/gems/src/app/themed_decorator/main.cc
Normal file
304
repos/gems/src/app/themed_decorator/main.cc
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* \brief Window decorator that can be styled
|
||||
* \author Norman Feske
|
||||
* \date 2015-11-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/signal.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <os/reporter.h>
|
||||
#include <os/server.h>
|
||||
|
||||
/* decorator includes */
|
||||
#include <decorator/window_stack.h>
|
||||
#include <decorator/xml_utils.h>
|
||||
|
||||
/* local includes */
|
||||
#include "window.h"
|
||||
|
||||
|
||||
namespace Decorator {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
|
||||
|
||||
struct Decorator::Main : Window_factory_base
|
||||
{
|
||||
Server::Entrypoint &ep;
|
||||
|
||||
Window_stack window_stack = { *this };
|
||||
|
||||
/**
|
||||
* Install handler for responding to window-layout changes
|
||||
*/
|
||||
void handle_window_layout_update(unsigned);
|
||||
|
||||
Signal_rpc_member<Main> window_layout_dispatcher = {
|
||||
ep, *this, &Main::handle_window_layout_update };
|
||||
|
||||
Attached_rom_dataspace window_layout { "window_layout" };
|
||||
|
||||
/**
|
||||
* Install handler for responding to pointer-position updates
|
||||
*/
|
||||
void handle_pointer_update(unsigned);
|
||||
|
||||
Signal_rpc_member<Main> pointer_dispatcher = {
|
||||
ep, *this, &Main::handle_pointer_update };
|
||||
|
||||
Attached_rom_dataspace pointer { "pointer" };
|
||||
|
||||
Window_base::Hover hover;
|
||||
|
||||
Reporter hover_reporter = { "hover" };
|
||||
|
||||
/**
|
||||
* Nitpicker connection used to sync animations
|
||||
*/
|
||||
Nitpicker::Connection nitpicker;
|
||||
|
||||
bool window_layout_update_needed = false;
|
||||
|
||||
Animator animator;
|
||||
|
||||
Theme theme { *Genode::env()->heap() };
|
||||
|
||||
/**
|
||||
* Process the update every 'frame_period' nitpicker sync signals. The
|
||||
* 'frame_cnt' holds the counter of the nitpicker sync signals.
|
||||
*
|
||||
* A lower 'frame_period' value makes the decorations more responsive
|
||||
* but it also puts more load on the system.
|
||||
*
|
||||
* If the nitpicker sync signal fires every 10 milliseconds, a
|
||||
* 'frame_period' of 2 results in an update rate of 1000/20 = 50 frames per
|
||||
* second.
|
||||
*/
|
||||
unsigned frame_cnt = 0;
|
||||
unsigned frame_period = 2;
|
||||
|
||||
/**
|
||||
* Install handler for responding to nitpicker sync events
|
||||
*/
|
||||
void handle_nitpicker_sync(unsigned);
|
||||
|
||||
Signal_rpc_member<Main> nitpicker_sync_dispatcher = {
|
||||
ep, *this, &Main::handle_nitpicker_sync };
|
||||
|
||||
Config config;
|
||||
|
||||
void handle_config(unsigned);
|
||||
|
||||
Signal_rpc_member<Main> config_dispatcher = {
|
||||
ep, *this, &Main::handle_config};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Main(Server::Entrypoint &ep) : ep(ep)
|
||||
{
|
||||
/*
|
||||
* Eagerly upgrade the session quota in order to be able to create a
|
||||
* high amount of view handles.
|
||||
*
|
||||
* XXX Consider upgrading the session quota on demand by responding
|
||||
* to Out_of_metadata exceptions raised by the create_view
|
||||
* and view_handle operations. Currently, these exceptions will
|
||||
* abort the decorator.
|
||||
*/
|
||||
Genode::env()->parent()->upgrade(nitpicker, "ram_quota=256K");
|
||||
|
||||
Genode::config()->sigh(config_dispatcher);
|
||||
handle_config(0);
|
||||
|
||||
window_layout.sigh(window_layout_dispatcher);
|
||||
pointer.sigh(pointer_dispatcher);
|
||||
|
||||
nitpicker.framebuffer()->sync_sigh(nitpicker_sync_dispatcher);
|
||||
|
||||
hover_reporter.enabled(true);
|
||||
|
||||
/* import initial state */
|
||||
handle_pointer_update(0);
|
||||
handle_window_layout_update(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Window_factory_base interface
|
||||
*/
|
||||
Window_base *create(Xml_node window_node) override
|
||||
{
|
||||
return new (env()->heap())
|
||||
Window(attribute(window_node, "id", 0UL), nitpicker, animator,
|
||||
*env()->ram_session(), theme, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Window_factory_base interface
|
||||
*/
|
||||
void destroy(Window_base *window) override
|
||||
{
|
||||
Genode::destroy(env()->heap(), static_cast<Window *>(window));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Decorator::Main::handle_config(unsigned)
|
||||
{
|
||||
Genode::config()->reload();
|
||||
|
||||
/* notify all windows to consider the updated policy */
|
||||
window_stack.for_each_window([&] (Window_base &window) {
|
||||
static_cast<Window &>(window).adapt_to_changed_config(); });
|
||||
|
||||
/* trigger redraw of the window stack */
|
||||
handle_window_layout_update(0);
|
||||
}
|
||||
|
||||
|
||||
static Decorator::Window_base::Hover
|
||||
find_hover(Genode::Xml_node pointer_node, Decorator::Window_stack &window_stack)
|
||||
{
|
||||
if (!pointer_node.has_attribute("xpos")
|
||||
|| !pointer_node.has_attribute("ypos"))
|
||||
return Decorator::Window_base::Hover();
|
||||
|
||||
return window_stack.hover(Decorator::point_attribute(pointer_node));
|
||||
}
|
||||
|
||||
|
||||
static void update_hover_report(Genode::Xml_node pointer_node,
|
||||
Decorator::Window_stack &window_stack,
|
||||
Decorator::Window_base::Hover &hover,
|
||||
Genode::Reporter &hover_reporter)
|
||||
{
|
||||
Decorator::Window_base::Hover const new_hover =
|
||||
find_hover(pointer_node, window_stack);
|
||||
|
||||
/* produce report only if hover state changed */
|
||||
if (new_hover != hover) {
|
||||
|
||||
hover = new_hover;
|
||||
|
||||
Genode::Reporter::Xml_generator xml(hover_reporter, [&] ()
|
||||
{
|
||||
if (hover.window_id > 0) {
|
||||
|
||||
xml.node("window", [&] () {
|
||||
|
||||
xml.attribute("id", hover.window_id);
|
||||
|
||||
if (hover.left_sizer) xml.node("left_sizer");
|
||||
if (hover.right_sizer) xml.node("right_sizer");
|
||||
if (hover.top_sizer) xml.node("top_sizer");
|
||||
if (hover.bottom_sizer) xml.node("bottom_sizer");
|
||||
if (hover.title) xml.node("title");
|
||||
if (hover.closer) xml.node("closer");
|
||||
if (hover.minimizer) xml.node("minimizer");
|
||||
if (hover.maximizer) xml.node("maximizer");
|
||||
if (hover.unmaximizer) xml.node("unmaximizer");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Main::handle_window_layout_update(unsigned)
|
||||
{
|
||||
window_layout.update();
|
||||
|
||||
window_layout_update_needed = true;
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Main::handle_nitpicker_sync(unsigned)
|
||||
{
|
||||
if (frame_cnt++ < frame_period)
|
||||
return;
|
||||
|
||||
frame_cnt = 0;
|
||||
|
||||
bool model_updated = false;
|
||||
|
||||
if (window_layout_update_needed && window_layout.is_valid()) {
|
||||
|
||||
try {
|
||||
Xml_node xml(window_layout.local_addr<char>(),
|
||||
window_layout.size());
|
||||
window_stack.update_model(xml);
|
||||
|
||||
model_updated = true;
|
||||
|
||||
/*
|
||||
* A decorator element might have appeared or disappeared under
|
||||
* the pointer.
|
||||
*/
|
||||
if (pointer.is_valid())
|
||||
update_hover_report(Xml_node(pointer.local_addr<char>()),
|
||||
window_stack, hover, hover_reporter);
|
||||
|
||||
} catch (Xml_node::Invalid_syntax) {
|
||||
|
||||
/*
|
||||
* An error occured with processing the XML model. Flush the
|
||||
* internal representation.
|
||||
*/
|
||||
window_stack.flush();
|
||||
}
|
||||
|
||||
window_layout_update_needed = false;
|
||||
}
|
||||
|
||||
bool const windows_animated = window_stack.schedule_animated_windows();
|
||||
|
||||
/*
|
||||
* To make the perceived animation speed independent from the setting of
|
||||
* 'frame_period', we update the animation as often as the nitpicker
|
||||
* sync signal occurs.
|
||||
*/
|
||||
for (unsigned i = 0; i < frame_period; i++)
|
||||
animator.animate();
|
||||
|
||||
if (!model_updated && !windows_animated)
|
||||
return;
|
||||
|
||||
window_stack.update_nitpicker_views();
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Main::handle_pointer_update(unsigned)
|
||||
{
|
||||
pointer.update();
|
||||
|
||||
if (pointer.is_valid())
|
||||
update_hover_report(Xml_node(pointer.local_addr<char>()),
|
||||
window_stack, hover, hover_reporter);
|
||||
}
|
||||
|
||||
|
||||
/************
|
||||
** Server **
|
||||
************/
|
||||
|
||||
namespace Server {
|
||||
|
||||
char const *name() { return "decorator_ep"; }
|
||||
|
||||
size_t stack_size() { return 8*1024*sizeof(long); }
|
||||
|
||||
void construct(Entrypoint &ep)
|
||||
{
|
||||
static Decorator::Main main(ep);
|
||||
}
|
||||
}
|
10
repos/gems/src/app/themed_decorator/target.mk
Normal file
10
repos/gems/src/app/themed_decorator/target.mk
Normal file
@ -0,0 +1,10 @@
|
||||
TARGET = themed_decorator
|
||||
SRC_CC = main.cc theme.cc window.cc
|
||||
LIBS = base config server libc libpng zlib blit file
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
||||
.PHONY: plain_decorator_theme.tar
|
||||
|
||||
$(TARGET): plain_decorator_theme.tar
|
||||
plain_decorator_theme.tar:
|
||||
$(VERBOSE)cd $(PRG_DIR); tar cf $(PWD)/bin/$@ theme
|
282
repos/gems/src/app/themed_decorator/theme.cc
Normal file
282
repos/gems/src/app/themed_decorator/theme.cc
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* \brief Window decorator that can be styled - theme handling
|
||||
* \author Norman Feske
|
||||
* \date 2015-11-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/texture_rgb888.h>
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <decorator/xml_utils.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/file.h>
|
||||
#include <gems/png_image.h>
|
||||
|
||||
/* demo includes */
|
||||
#include <scout_gfx/icon_painter.h>
|
||||
|
||||
/* local includes */
|
||||
#include "theme.h"
|
||||
|
||||
|
||||
enum Texture_id { TEXTURE_ID_DEFAULT, TEXTURE_ID_CLOSER, TEXTURE_ID_MAXIMIZER };
|
||||
|
||||
|
||||
struct Texture_from_png_file
|
||||
{
|
||||
typedef Genode::Texture<Genode::Pixel_rgb888> Texture;
|
||||
|
||||
File png_file;
|
||||
Png_image png_image { png_file.data<void>() };
|
||||
Texture &texture { *png_image.texture<Genode::Pixel_rgb888>() };
|
||||
|
||||
Texture_from_png_file(char const *path, Genode::Allocator &alloc)
|
||||
:
|
||||
png_file(path, alloc)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
static Genode::Texture<Genode::Pixel_rgb888> const &
|
||||
texture_by_id(Texture_id texture_id, Genode::Allocator &alloc)
|
||||
{
|
||||
if (texture_id == TEXTURE_ID_DEFAULT) {
|
||||
static Texture_from_png_file texture("theme/default.png", alloc);
|
||||
return texture.texture;
|
||||
}
|
||||
|
||||
if (texture_id == TEXTURE_ID_CLOSER) {
|
||||
static Texture_from_png_file texture("theme/closer.png", alloc);
|
||||
return texture.texture;
|
||||
}
|
||||
|
||||
if (texture_id == TEXTURE_ID_MAXIMIZER) {
|
||||
static Texture_from_png_file texture("theme/maximizer.png", alloc);
|
||||
return texture.texture;
|
||||
}
|
||||
|
||||
struct Invalid_texture_id { };
|
||||
throw Invalid_texture_id();
|
||||
}
|
||||
|
||||
|
||||
static Genode::Texture<Genode::Pixel_rgb888> const &
|
||||
texture_by_element_type(Decorator::Theme::Element_type type, Genode::Allocator &alloc)
|
||||
{
|
||||
switch (type) {
|
||||
case Decorator::Theme::ELEMENT_TYPE_CLOSER:
|
||||
return texture_by_id(TEXTURE_ID_CLOSER, alloc);
|
||||
|
||||
case Decorator::Theme::ELEMENT_TYPE_MAXIMIZER:
|
||||
return texture_by_id(TEXTURE_ID_MAXIMIZER, alloc);
|
||||
}
|
||||
struct Invalid_element_type { };
|
||||
throw Invalid_element_type();
|
||||
};
|
||||
|
||||
|
||||
static Text_painter::Font const &title_font(Genode::Allocator &alloc)
|
||||
{
|
||||
static File tff_file("theme/font.tff", alloc);
|
||||
static Text_painter::Font font(tff_file.data<char>());
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
static Genode::Xml_node metadata(Genode::Allocator &alloc)
|
||||
{
|
||||
static File file("theme/metadata", alloc);
|
||||
|
||||
return Genode::Xml_node(file.data<char>(), file.size());
|
||||
}
|
||||
|
||||
|
||||
Decorator::Area Decorator::Theme::background_size() const
|
||||
{
|
||||
Genode::Texture<Pixel_rgb888> const &texture = texture_by_id(TEXTURE_ID_DEFAULT, _alloc);
|
||||
|
||||
return texture.size();
|
||||
}
|
||||
|
||||
|
||||
struct Margins_from_metadata : Decorator::Theme::Margins
|
||||
{
|
||||
Margins_from_metadata(char const *sub_node, Genode::Allocator &alloc)
|
||||
{
|
||||
Genode::Xml_node aura = metadata(alloc).sub_node(sub_node);
|
||||
top = aura.attribute_value("top", 0UL);
|
||||
bottom = aura.attribute_value("bottom", 0UL);
|
||||
left = aura.attribute_value("left", 0UL);
|
||||
right = aura.attribute_value("right", 0UL);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Decorator::Theme::Margins Decorator::Theme::aura_margins() const
|
||||
{
|
||||
static Margins_from_metadata aura("aura", _alloc);
|
||||
return aura;
|
||||
}
|
||||
|
||||
|
||||
Decorator::Theme::Margins Decorator::Theme::decor_margins() const
|
||||
{
|
||||
static Margins_from_metadata decor("decor", _alloc);
|
||||
return decor;
|
||||
}
|
||||
|
||||
|
||||
Decorator::Rect Decorator::Theme::title_geometry() const
|
||||
{
|
||||
static Rect rect = rect_attribute(metadata(_alloc).sub_node("title"));
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
Decorator::Rect Decorator::Theme::element_geometry(Element_type type) const
|
||||
{
|
||||
if (type == ELEMENT_TYPE_CLOSER) {
|
||||
static Rect rect(point_attribute(metadata(_alloc).sub_node("closer")),
|
||||
texture_by_id(TEXTURE_ID_CLOSER, _alloc).size());
|
||||
return rect;
|
||||
}
|
||||
|
||||
if (type == ELEMENT_TYPE_MAXIMIZER) {
|
||||
static Rect rect(point_attribute(metadata(_alloc).sub_node("maximizer")),
|
||||
texture_by_id(TEXTURE_ID_MAXIMIZER, _alloc).size());
|
||||
return rect;
|
||||
}
|
||||
|
||||
struct Invalid_element_type { };
|
||||
throw Invalid_element_type();
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Theme::draw_background(Decorator::Pixel_surface pixel_surface,
|
||||
Decorator::Alpha_surface alpha_surface,
|
||||
unsigned alpha) const
|
||||
{
|
||||
Genode::Texture<Pixel_rgb888> const &texture = texture_by_id(TEXTURE_ID_DEFAULT, _alloc);
|
||||
|
||||
typedef Genode::Surface_base::Point Point;
|
||||
typedef Genode::Surface_base::Rect Rect;
|
||||
|
||||
unsigned const left = aura_margins().left + decor_margins().left;
|
||||
unsigned const right = aura_margins().right + decor_margins().right;
|
||||
|
||||
unsigned const middle = left + right < pixel_surface.size().w()
|
||||
? pixel_surface.size().w() - left - right
|
||||
: 0;
|
||||
|
||||
Rect const orig_clip = pixel_surface.clip();
|
||||
|
||||
/* left */
|
||||
if (left) {
|
||||
Rect curr_clip = Rect(Point(0, 0), Area(left, pixel_surface.size().h()));
|
||||
pixel_surface.clip(curr_clip);
|
||||
alpha_surface.clip(curr_clip);
|
||||
|
||||
Rect const rect(Point(0, 0), pixel_surface.size());
|
||||
|
||||
Icon_painter::paint(pixel_surface, rect, texture, alpha);
|
||||
Icon_painter::paint(alpha_surface, rect, texture, alpha);
|
||||
}
|
||||
|
||||
/* middle */
|
||||
if (middle) {
|
||||
Rect curr_clip = Rect(Point(left, 0), Area(middle, pixel_surface.size().h()));
|
||||
pixel_surface.clip(curr_clip);
|
||||
alpha_surface.clip(curr_clip);
|
||||
|
||||
Rect const rect(Point(0, 0), pixel_surface.size());
|
||||
|
||||
Icon_painter::paint(pixel_surface, rect, texture, alpha);
|
||||
Icon_painter::paint(alpha_surface, rect, texture, alpha);
|
||||
}
|
||||
|
||||
/* right */
|
||||
if (right) {
|
||||
Rect curr_clip = Rect(Point(left + middle, 0), Area(right, pixel_surface.size().h()));
|
||||
pixel_surface.clip(curr_clip);
|
||||
alpha_surface.clip(curr_clip);
|
||||
|
||||
Point at(0, 0);
|
||||
Area size = pixel_surface.size();
|
||||
|
||||
if (texture.size().w() > pixel_surface.size().w()) {
|
||||
at = Point((int)pixel_surface.size().w() - (int)texture.size().w(), 0);
|
||||
size = Area(texture.size().w(), size.h());
|
||||
}
|
||||
|
||||
Icon_painter::paint(pixel_surface, Rect(at, size), texture, alpha);
|
||||
Icon_painter::paint(alpha_surface, Rect(at, size), texture, alpha);
|
||||
}
|
||||
|
||||
pixel_surface.clip(orig_clip);
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Theme::draw_title(Decorator::Pixel_surface pixel_surface,
|
||||
Decorator::Alpha_surface alpha_surface,
|
||||
char const *title) const
|
||||
{
|
||||
Text_painter::Font const &font = title_font(_alloc);
|
||||
|
||||
Area const label_area(font.str_w(title), font.str_h(title));
|
||||
Rect const surface_rect(Point(0, 0), pixel_surface.size());
|
||||
Rect const title_rect = absolute(title_geometry(), surface_rect);
|
||||
Point const centered_text_pos = title_rect.center(label_area) - Point(0, 1);
|
||||
|
||||
Text_painter::paint(pixel_surface, centered_text_pos, font,
|
||||
Genode::Color(0, 0, 0), title);
|
||||
}
|
||||
|
||||
|
||||
void Decorator::Theme::draw_element(Decorator::Pixel_surface pixel_surface,
|
||||
Decorator::Alpha_surface alpha_surface,
|
||||
Element_type element_type,
|
||||
unsigned alpha) const
|
||||
{
|
||||
Genode::Texture<Pixel_rgb888> const &texture =
|
||||
texture_by_element_type(element_type, _alloc);
|
||||
|
||||
Rect const surface_rect(Point(0, 0), pixel_surface.size());
|
||||
Rect const element_rect = element_geometry(element_type);
|
||||
Point const pos = absolute(element_rect.p1(), surface_rect);
|
||||
Rect const rect(pos, element_rect.area());
|
||||
|
||||
Icon_painter::paint(pixel_surface, rect, texture, alpha);
|
||||
Icon_painter::paint(alpha_surface, rect, texture, alpha);
|
||||
}
|
||||
|
||||
|
||||
Decorator::Point Decorator::Theme::absolute(Decorator::Point pos,
|
||||
Decorator::Rect win_rect) const
|
||||
{
|
||||
Area const theme_size = background_size();
|
||||
|
||||
int x = pos.x();
|
||||
int y = pos.y();
|
||||
|
||||
if (x > (int)theme_size.w()/2) x = win_rect.w() - theme_size.w() + x;
|
||||
if (y > (int)theme_size.h()/2) y = win_rect.h() - theme_size.h() + y;
|
||||
|
||||
return win_rect.p1() + Point(x, y);
|
||||
}
|
||||
|
||||
|
||||
Decorator::Rect Decorator::Theme::absolute(Decorator::Rect rect,
|
||||
Decorator::Rect win_rect) const
|
||||
{
|
||||
return Rect(absolute(rect.p1(), win_rect), absolute(rect.p2(), win_rect));
|
||||
}
|
85
repos/gems/src/app/themed_decorator/theme.h
Normal file
85
repos/gems/src/app/themed_decorator/theme.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* \brief Window decorator that can be styled - theme handling
|
||||
* \author Norman Feske
|
||||
* \date 2015-11-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _THEME_H_
|
||||
#define _THEME_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/texture.h>
|
||||
#include <os/pixel_rgb565.h>
|
||||
#include <os/pixel_alpha8.h>
|
||||
#include <os/pixel_rgb888.h>
|
||||
|
||||
namespace Decorator {
|
||||
|
||||
class Theme;
|
||||
|
||||
typedef Genode::Pixel_rgb888 Pixel_rgb888;
|
||||
typedef Genode::Pixel_rgb565 Pixel_rgb565;
|
||||
typedef Genode::Pixel_alpha8 Pixel_alpha8;
|
||||
|
||||
typedef Genode::Surface<Pixel_rgb888> Pixel_surface;
|
||||
typedef Genode::Surface<Pixel_alpha8> Alpha_surface;
|
||||
|
||||
typedef Genode::Surface_base::Area Area;
|
||||
typedef Genode::Surface_base::Point Point;
|
||||
typedef Genode::Surface_base::Rect Rect;
|
||||
}
|
||||
|
||||
|
||||
class Decorator::Theme
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
|
||||
public:
|
||||
|
||||
struct Margins
|
||||
{
|
||||
unsigned top, bottom, left, right;
|
||||
};
|
||||
|
||||
enum Element_type { ELEMENT_TYPE_CLOSER, ELEMENT_TYPE_MAXIMIZER };
|
||||
|
||||
Theme(Genode::Allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
Area background_size() const;
|
||||
|
||||
Margins aura_margins() const;
|
||||
|
||||
Margins decor_margins() const;
|
||||
|
||||
void draw_background(Pixel_surface, Alpha_surface, unsigned alpha) const;
|
||||
|
||||
void draw_title(Pixel_surface, Alpha_surface, char const *title) const;
|
||||
|
||||
void draw_element(Pixel_surface, Alpha_surface, Element_type,
|
||||
unsigned alpha) const;
|
||||
|
||||
/**
|
||||
* Return geometry of theme elements in the theme coordinate space
|
||||
*/
|
||||
Rect title_geometry() const;
|
||||
Rect element_geometry(Element_type) const;
|
||||
|
||||
/**
|
||||
* Calculate screen-absolute coordinate for a position within the theme
|
||||
* coordinate space
|
||||
*/
|
||||
Point absolute(Point theme_pos, Rect win_outer_geometry) const;
|
||||
|
||||
Rect absolute(Rect theme_rect, Rect win_outer_geometry) const;
|
||||
};
|
||||
|
||||
#endif /* _THEME_H_ */
|
BIN
repos/gems/src/app/themed_decorator/theme/closer.png
Normal file
BIN
repos/gems/src/app/themed_decorator/theme/closer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 670 B |
BIN
repos/gems/src/app/themed_decorator/theme/default.png
Normal file
BIN
repos/gems/src/app/themed_decorator/theme/default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
repos/gems/src/app/themed_decorator/theme/font.tff
Normal file
BIN
repos/gems/src/app/themed_decorator/theme/font.tff
Normal file
Binary file not shown.
BIN
repos/gems/src/app/themed_decorator/theme/maximizer.png
Normal file
BIN
repos/gems/src/app/themed_decorator/theme/maximizer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 653 B |
7
repos/gems/src/app/themed_decorator/theme/metadata
Normal file
7
repos/gems/src/app/themed_decorator/theme/metadata
Normal file
@ -0,0 +1,7 @@
|
||||
<theme>
|
||||
<aura top="8" bottom="8" left="8" right="8"/>
|
||||
<decor top="20" bottom="8" left="1" right="1"/>
|
||||
<title xpos="16" ypos="9" width="32" height="20"/>
|
||||
<closer xpos="36" ypos="10"/>
|
||||
<maximizer xpos="10" ypos="10"/>
|
||||
</theme>
|
73
repos/gems/src/app/themed_decorator/tint_painter.h
Normal file
73
repos/gems/src/app/themed_decorator/tint_painter.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* \brief Functor for tinting a surface with a color
|
||||
* \author Norman Feske
|
||||
* \date 2015-11-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _TINT_PAINTER_H_
|
||||
#define _TINT_PAINTER_H_
|
||||
|
||||
#include <os/surface.h>
|
||||
#include <polygon_gfx/interpolate_rgba.h>
|
||||
|
||||
|
||||
struct Tint_painter
|
||||
{
|
||||
typedef Genode::Surface_base::Rect Rect;
|
||||
|
||||
/**
|
||||
* Tint box with specified color
|
||||
*
|
||||
* \param rect position and size of box to tint
|
||||
* \param color tinting color
|
||||
*/
|
||||
template <typename PT>
|
||||
static inline void paint(Genode::Surface<PT> surface,
|
||||
Rect rect,
|
||||
Genode::Color color)
|
||||
{
|
||||
Rect clipped = Rect::intersect(surface.clip(), rect);
|
||||
|
||||
if (!clipped.valid()) return;
|
||||
|
||||
/*
|
||||
* Generate lookup table (LUT) for mapping brightness values to colors.
|
||||
* The specified color is used as a fixed point within the LUT. The
|
||||
* other values are interpolated from black over the color to white.
|
||||
*/
|
||||
enum { LUT_SIZE = 256*3 };
|
||||
PT pixel_lut[LUT_SIZE];
|
||||
unsigned char alpha_lut[LUT_SIZE];
|
||||
|
||||
unsigned const lut_idx = color.r + color.g + color.b;
|
||||
|
||||
Polygon::interpolate_rgba(Polygon::Color(0, 0, 0), color,
|
||||
pixel_lut, alpha_lut,
|
||||
lut_idx + 1, 0, 0);
|
||||
|
||||
Polygon::interpolate_rgba(color, Polygon::Color(255, 255, 255),
|
||||
pixel_lut + lut_idx, alpha_lut + lut_idx,
|
||||
LUT_SIZE - lut_idx, 0, 0);
|
||||
|
||||
|
||||
PT pix(color.r, color.g, color.b);
|
||||
PT *dst, *dst_line = surface.addr() + surface.size().w()*clipped.y1() + clipped.x1();
|
||||
|
||||
for (int w, h = clipped.h() ; h--; dst_line += surface.size().w())
|
||||
for (dst = dst_line, w = clipped.w(); w--; dst++) {
|
||||
PT const pixel = *dst;
|
||||
*dst = pixel_lut[pixel.r() + pixel.g() + pixel.b()];
|
||||
}
|
||||
|
||||
surface.flush_pixels(clipped);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__NITPICKER_GFX__BOX_PAINTER_H_ */
|
60
repos/gems/src/app/themed_decorator/window.cc
Normal file
60
repos/gems/src/app/themed_decorator/window.cc
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* \brief Example window decorator that mimics the Motif look
|
||||
* \author Norman Feske
|
||||
* \date 2014-01-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include "window.h"
|
||||
|
||||
Decorator::Window_base::Hover Decorator::Window::hover(Point abs_pos) const
|
||||
{
|
||||
Hover hover;
|
||||
|
||||
if (!_decor_geometry().contains(abs_pos))
|
||||
return hover;
|
||||
|
||||
hover.window_id = id();
|
||||
|
||||
Rect const closer_geometry =
|
||||
_theme.absolute(_theme.element_geometry(Theme::ELEMENT_TYPE_CLOSER),
|
||||
outer_geometry());
|
||||
if (closer_geometry.contains(abs_pos)) {
|
||||
hover.closer = true;
|
||||
return hover;
|
||||
}
|
||||
|
||||
Rect const maximizer_geometry =
|
||||
_theme.absolute(_theme.element_geometry(Theme::ELEMENT_TYPE_MAXIMIZER),
|
||||
outer_geometry());
|
||||
if (maximizer_geometry.contains(abs_pos)) {
|
||||
hover.maximizer = true;
|
||||
return hover;
|
||||
}
|
||||
|
||||
Rect const title_geometry = _theme.absolute(_theme.title_geometry(),
|
||||
outer_geometry());
|
||||
if (title_geometry.contains(abs_pos)) {
|
||||
hover.title = true;
|
||||
return hover;
|
||||
}
|
||||
|
||||
int const x = abs_pos.x();
|
||||
int const y = abs_pos.y();
|
||||
|
||||
Area const theme_size = _theme.background_size();
|
||||
|
||||
hover.left_sizer = x < outer_geometry().x1() + (int)theme_size.w()/2;
|
||||
hover.right_sizer = x > outer_geometry().x2() - (int)theme_size.w()/2;
|
||||
hover.top_sizer = y < outer_geometry().y1() + (int)theme_size.h()/2;
|
||||
hover.bottom_sizer = y > outer_geometry().y2() - (int)theme_size.h()/2;
|
||||
|
||||
return hover;
|
||||
}
|
539
repos/gems/src/app/themed_decorator/window.h
Normal file
539
repos/gems/src/app/themed_decorator/window.h
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* \brief Window decorator that can be styled
|
||||
* \author Norman Feske
|
||||
* \date 2015-11-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _WINDOW_H_
|
||||
#define _WINDOW_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <ram_session/ram_session.h>
|
||||
#include <decorator/window.h>
|
||||
#include <nitpicker_session/connection.h>
|
||||
#include <os/attached_dataspace.h>
|
||||
#include <util/volatile_object.h>
|
||||
|
||||
/* demo includes */
|
||||
#include <util/lazy_value.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/animator.h>
|
||||
#include <gems/nitpicker_buffer.h>
|
||||
|
||||
/* local includes */
|
||||
#include "theme.h"
|
||||
#include "config.h"
|
||||
#include "tint_painter.h"
|
||||
|
||||
namespace Decorator {
|
||||
|
||||
class Window;
|
||||
typedef Genode::String<200> Window_title;
|
||||
typedef Genode::Attached_dataspace Attached_dataspace;
|
||||
}
|
||||
|
||||
|
||||
class Decorator::Window : public Window_base, public Animator::Item
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Ram_session &_ram;
|
||||
|
||||
Theme const &_theme;
|
||||
|
||||
/*
|
||||
* Flag indicating that the current window position has been propagated
|
||||
* to the window's corresponding nitpicker views.
|
||||
*/
|
||||
bool _nitpicker_views_up_to_date = false;
|
||||
|
||||
Nitpicker::Session::View_handle _neighbor;
|
||||
|
||||
unsigned _topped_cnt = 0;
|
||||
|
||||
Window_title _title;
|
||||
|
||||
bool _focused = false;
|
||||
|
||||
Lazy_value<int> _alpha = 0;
|
||||
|
||||
Animator &_animator;
|
||||
|
||||
struct Element : Animator::Item
|
||||
{
|
||||
Theme::Element_type const type;
|
||||
|
||||
char const * const attr;
|
||||
|
||||
bool _highlighted = false;
|
||||
bool _present = false;
|
||||
|
||||
Lazy_value<int> alpha = 0;
|
||||
|
||||
int _alpha_dst() const
|
||||
{
|
||||
if (!_present)
|
||||
return 0;
|
||||
|
||||
return _highlighted ? 255 : 150;
|
||||
}
|
||||
|
||||
void _update_alpha_dst()
|
||||
{
|
||||
if ((int)alpha == _alpha_dst())
|
||||
return;
|
||||
|
||||
alpha.dst(_alpha_dst(), 20);
|
||||
animate();
|
||||
}
|
||||
|
||||
void highlighted(bool highlighted)
|
||||
{
|
||||
_highlighted = highlighted;
|
||||
_update_alpha_dst();
|
||||
}
|
||||
|
||||
bool highlighted() const { return _highlighted; }
|
||||
|
||||
void present(bool present)
|
||||
{
|
||||
_present = present;
|
||||
_update_alpha_dst();
|
||||
}
|
||||
|
||||
bool present() const { return _present; }
|
||||
|
||||
void animate() override
|
||||
{
|
||||
alpha.animate();
|
||||
animated((int)alpha != alpha.dst());
|
||||
}
|
||||
|
||||
Element(Animator &animator, Theme::Element_type type, char const *attr)
|
||||
:
|
||||
Animator::Item(animator),
|
||||
type(type), attr(attr)
|
||||
{
|
||||
_update_alpha_dst();
|
||||
}
|
||||
};
|
||||
|
||||
Element _closer { _animator, Theme::ELEMENT_TYPE_CLOSER, "closer" };
|
||||
Element _maximizer { _animator, Theme::ELEMENT_TYPE_MAXIMIZER, "maximizer" };
|
||||
|
||||
template <typename FN>
|
||||
void _for_each_element(FN const &func)
|
||||
{
|
||||
func(_closer);
|
||||
func(_maximizer);
|
||||
}
|
||||
|
||||
struct Nitpicker_view
|
||||
{
|
||||
typedef Nitpicker::Session::Command Command;
|
||||
typedef Nitpicker::Session::View_handle View_handle;
|
||||
|
||||
bool const _view_is_remote;
|
||||
|
||||
Nitpicker::Session_client &_nitpicker;
|
||||
|
||||
View_handle _handle;
|
||||
|
||||
Nitpicker_view(Nitpicker::Session_client &nitpicker,
|
||||
unsigned id = 0)
|
||||
:
|
||||
_view_is_remote(false),
|
||||
_nitpicker(nitpicker),
|
||||
_handle(_nitpicker.create_view())
|
||||
{
|
||||
/*
|
||||
* We supply the window ID as label for the anchor view.
|
||||
*/
|
||||
if (id) {
|
||||
char buf[128];
|
||||
Genode::snprintf(buf, sizeof(buf), "%d", id);
|
||||
|
||||
_nitpicker.enqueue<Command::Title>(_handle, buf);
|
||||
}
|
||||
}
|
||||
|
||||
View_handle _create_remote_view(Nitpicker::Session_client &remote_nitpicker)
|
||||
{
|
||||
/* create view at the remote nitpicker session */
|
||||
View_handle handle = remote_nitpicker.create_view();
|
||||
Nitpicker::View_capability view_cap = remote_nitpicker.view_capability(handle);
|
||||
|
||||
/* import remote view into local nitpicker session */
|
||||
return _nitpicker.view_handle(view_cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called for creating a view that refers to a buffer
|
||||
* of another nitpicker session
|
||||
*/
|
||||
Nitpicker_view(Nitpicker::Session_client &nitpicker,
|
||||
Nitpicker::Session_client &remote_nitpicker)
|
||||
:
|
||||
_view_is_remote(true),
|
||||
_nitpicker(nitpicker),
|
||||
_handle(_create_remote_view(remote_nitpicker))
|
||||
{ }
|
||||
|
||||
~Nitpicker_view()
|
||||
{
|
||||
if (_view_is_remote)
|
||||
_nitpicker.release_view_handle(_handle);
|
||||
else
|
||||
_nitpicker.destroy_view(_handle);
|
||||
}
|
||||
|
||||
View_handle handle() const { return _handle; }
|
||||
|
||||
void stack(View_handle neighbor)
|
||||
{
|
||||
_nitpicker.enqueue<Command::To_front>(_handle, neighbor);
|
||||
}
|
||||
|
||||
void place(Rect rect, Point offset)
|
||||
{
|
||||
_nitpicker.enqueue<Command::Geometry>(_handle, rect);
|
||||
_nitpicker.enqueue<Command::Offset>(_handle, offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Nitpicker used as a global namespace of view handles
|
||||
*/
|
||||
Nitpicker::Session_client &_nitpicker;
|
||||
|
||||
Config const &_config;
|
||||
|
||||
Color _base_color = _config.base_color(_title);
|
||||
|
||||
/*
|
||||
* Color value in 8.4 fixpoint format. We use four bits to
|
||||
* represent the fractional part to enable smooth
|
||||
* interpolation between the color values.
|
||||
*/
|
||||
Lazy_value<int> _r, _g, _b;
|
||||
|
||||
Color _color() const { return Color(_r >> 4, _g >> 4, _b >> 4); }
|
||||
|
||||
/**
|
||||
* Nitpicker session that contains the upper and lower window
|
||||
* decorations.
|
||||
*/
|
||||
Nitpicker::Connection _nitpicker_top_bottom;
|
||||
Genode::Lazy_volatile_object<Nitpicker_buffer> _buffer_top_bottom;
|
||||
|
||||
/**
|
||||
* Nitpicker session that contains the left and right window
|
||||
* decorations.
|
||||
*/
|
||||
Nitpicker::Connection _nitpicker_left_right;
|
||||
Genode::Lazy_volatile_object<Nitpicker_buffer> _buffer_left_right;
|
||||
|
||||
Nitpicker_view _bottom_view { _nitpicker, _nitpicker_top_bottom },
|
||||
_right_view { _nitpicker, _nitpicker_left_right },
|
||||
_left_view { _nitpicker, _nitpicker_left_right },
|
||||
_top_view { _nitpicker, _nitpicker_top_bottom };
|
||||
|
||||
Nitpicker_view _content_view { _nitpicker, (unsigned)id() };
|
||||
|
||||
void _reallocate_nitpicker_buffers()
|
||||
{
|
||||
Area const theme_size = _theme.background_size();
|
||||
bool const use_alpha = true;
|
||||
|
||||
Area const inner_size = geometry().area();
|
||||
Area const outer_size = outer_geometry().area();
|
||||
|
||||
Area const size_top_bottom(outer_size.w(),
|
||||
theme_size.h());
|
||||
|
||||
_nitpicker_top_bottom.buffer(Framebuffer::Mode(size_top_bottom.w(),
|
||||
size_top_bottom.h(),
|
||||
Framebuffer::Mode::RGB565),
|
||||
use_alpha);
|
||||
|
||||
_buffer_top_bottom.construct(_nitpicker_top_bottom, size_top_bottom, _ram);
|
||||
|
||||
Area const size_left_right(outer_size.w() - inner_size.w(),
|
||||
outer_size.h());
|
||||
|
||||
_nitpicker_left_right.buffer(Framebuffer::Mode(size_left_right.w(),
|
||||
size_left_right.h(),
|
||||
Framebuffer::Mode::RGB565),
|
||||
use_alpha);
|
||||
|
||||
_buffer_left_right.construct(_nitpicker_left_right, size_left_right, _ram);
|
||||
}
|
||||
|
||||
void _repaint_decorations(Nitpicker_buffer &buffer)
|
||||
{
|
||||
buffer.reset_surface();
|
||||
|
||||
_theme.draw_background(buffer.pixel_surface(),
|
||||
buffer.alpha_surface(),
|
||||
(int)_alpha);
|
||||
|
||||
_theme.draw_title(buffer.pixel_surface(), buffer.alpha_surface(),
|
||||
_title.string());
|
||||
|
||||
_for_each_element([&] (Element const &element) {
|
||||
_theme.draw_element(buffer.pixel_surface(), buffer.alpha_surface(),
|
||||
element.type, element.alpha); });
|
||||
|
||||
Color const tint_color = _color();
|
||||
if (tint_color != Color(0, 0, 0))
|
||||
Tint_painter::paint(buffer.pixel_surface(),
|
||||
Rect(Point(0, 0), buffer.pixel_surface().size()),
|
||||
tint_color);
|
||||
|
||||
buffer.flush_surface();
|
||||
|
||||
buffer.nitpicker.framebuffer()->refresh(0, 0, buffer.size().w(), buffer.size().h());
|
||||
}
|
||||
|
||||
void _assign_color(Color color)
|
||||
{
|
||||
_base_color = color;
|
||||
|
||||
_r.dst(_base_color.r << 4, 20);
|
||||
_g.dst(_base_color.g << 4, 20);
|
||||
_b.dst(_base_color.b << 4, 20);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Window(unsigned id, Nitpicker::Session_client &nitpicker,
|
||||
Animator &animator, Genode::Ram_session &ram,
|
||||
Theme const &theme, Config const &config)
|
||||
:
|
||||
Window_base(id),
|
||||
Animator::Item(animator),
|
||||
_ram(ram), _theme(theme), _animator(animator),
|
||||
_nitpicker(nitpicker), _config(config)
|
||||
{
|
||||
_reallocate_nitpicker_buffers();
|
||||
_alpha.dst(_focused ? 256 : 200, 20);
|
||||
animate();
|
||||
}
|
||||
|
||||
void stack(Nitpicker::Session::View_handle neighbor) override
|
||||
{
|
||||
_neighbor = neighbor;
|
||||
|
||||
_top_view.stack(neighbor);
|
||||
_left_view.stack(_top_view.handle());
|
||||
_right_view.stack(_left_view.handle());
|
||||
_bottom_view.stack(_right_view.handle());
|
||||
_content_view.stack(_bottom_view.handle());
|
||||
}
|
||||
|
||||
Nitpicker::Session::View_handle frontmost_view() const override
|
||||
{
|
||||
return _content_view.handle();
|
||||
}
|
||||
|
||||
Rect _decor_geometry() const
|
||||
{
|
||||
Theme::Margins const decor = _theme.decor_margins();
|
||||
|
||||
return Rect(geometry().p1() - Point(decor.left, decor.top),
|
||||
geometry().p2() + Point(decor.right, decor.bottom));
|
||||
}
|
||||
|
||||
Rect outer_geometry() const override
|
||||
{
|
||||
Theme::Margins const aura = _theme.aura_margins();
|
||||
Theme::Margins const decor = _theme.decor_margins();
|
||||
|
||||
unsigned const left = aura.left + decor.left;
|
||||
unsigned const right = aura.right + decor.right;
|
||||
unsigned const top = aura.top + decor.top;
|
||||
unsigned const bottom = aura.bottom + decor.bottom;
|
||||
|
||||
return Rect(geometry().p1() - Point(left, top),
|
||||
geometry().p2() + Point(right, bottom));
|
||||
}
|
||||
|
||||
void border_rects(Rect *top, Rect *left, Rect *right, Rect *bottom) const
|
||||
{
|
||||
outer_geometry().cut(geometry(), top, left, right, bottom);
|
||||
}
|
||||
|
||||
bool is_in_front_of(Window_base const &neighbor) const override
|
||||
{
|
||||
return _neighbor == neighbor.frontmost_view();
|
||||
}
|
||||
|
||||
void update_nitpicker_views() override
|
||||
{
|
||||
if (!_nitpicker_views_up_to_date) {
|
||||
|
||||
Area const theme_size = _theme.background_size();
|
||||
|
||||
/* update view positions */
|
||||
Rect top, left, right, bottom;
|
||||
border_rects(&top, &left, &right, &bottom);
|
||||
|
||||
_content_view.place(geometry(), Point(0, 0));
|
||||
_top_view .place(top, Point(0, 0));
|
||||
_left_view .place(left, Point(0, -top.h()));
|
||||
_right_view .place(right, Point(-right.w(), -top.h()));
|
||||
_bottom_view .place(bottom, Point(0, -theme_size.h() + bottom.h()));
|
||||
|
||||
_nitpicker_views_up_to_date = true;
|
||||
}
|
||||
_nitpicker.execute();
|
||||
}
|
||||
|
||||
void draw(Canvas_base &, Rect, Draw_behind_fn const &) const override { }
|
||||
|
||||
void adapt_to_changed_config()
|
||||
{
|
||||
_assign_color(_config.base_color(_title));
|
||||
animate();
|
||||
}
|
||||
|
||||
bool update(Xml_node window_node) override
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
/*
|
||||
* Detect the need to bring the window to the top of the global
|
||||
* view stack.
|
||||
*/
|
||||
unsigned const topped_cnt = attribute(window_node, "topped", 0UL);
|
||||
if (topped_cnt != _topped_cnt) {
|
||||
|
||||
_topped_cnt = topped_cnt;
|
||||
|
||||
stack(Nitpicker::Session::View_handle());
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
bool trigger_animation = false;
|
||||
|
||||
Rect const old_geometry = geometry();
|
||||
|
||||
geometry(rect_attribute(window_node));
|
||||
|
||||
/*
|
||||
* Detect position changes
|
||||
*/
|
||||
if (geometry().p1() != old_geometry.p1()
|
||||
|| geometry().p2() != old_geometry.p2()) {
|
||||
|
||||
_nitpicker_views_up_to_date = false;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect size changes
|
||||
*/
|
||||
if (geometry().w() != old_geometry.w()
|
||||
|| geometry().h() != old_geometry.h()) {
|
||||
|
||||
_reallocate_nitpicker_buffers();
|
||||
|
||||
/* triggering the animation has the side effect of repainting */
|
||||
trigger_animation = true;
|
||||
}
|
||||
|
||||
bool focused = window_node.attribute_value("focused", false);
|
||||
|
||||
if (_focused != focused) {
|
||||
_focused = focused;
|
||||
_alpha.dst(_focused ? 256 : 200, 20);
|
||||
trigger_animation = true;
|
||||
}
|
||||
|
||||
Window_title title = Decorator::string_attribute(window_node, "title",
|
||||
Window_title("<untitled>"));
|
||||
|
||||
if (_title != title) {
|
||||
_title = title;
|
||||
trigger_animation = true;
|
||||
}
|
||||
|
||||
/* update color on title change as the title is used as policy selector */
|
||||
Color const base_color = _config.base_color(_title);
|
||||
if (_base_color != base_color) {
|
||||
_assign_color(base_color);
|
||||
trigger_animation = true;
|
||||
}
|
||||
|
||||
_for_each_element([&] (Element &element) {
|
||||
bool const present = window_node.attribute_value(element.attr, false);
|
||||
if (present != element.present()) {
|
||||
element.present(present);
|
||||
trigger_animation = true;
|
||||
}
|
||||
});
|
||||
|
||||
Xml_node const highlight = window_node.has_sub_node("highlight")
|
||||
? window_node.sub_node("highlight")
|
||||
: Xml_node("<highlight/>");
|
||||
|
||||
_for_each_element([&] (Element &element) {
|
||||
bool const highlighted = highlight.has_sub_node(element.attr);
|
||||
if (highlighted != element.highlighted()) {
|
||||
element.highlighted(highlighted);
|
||||
trigger_animation = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (trigger_animation) {
|
||||
updated = true;
|
||||
|
||||
/* schedule animation */
|
||||
animate();
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
Hover hover(Point) const override;
|
||||
|
||||
/**
|
||||
* Window_base interface
|
||||
*/
|
||||
bool animated() const override
|
||||
{
|
||||
return (_alpha.dst() != (int)_alpha)
|
||||
|| _r != _r.dst() || _g != _g.dst() || _b != _b.dst()
|
||||
|| _closer.animated() || _maximizer.animated();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Animator::Item interface
|
||||
*/
|
||||
void animate() override
|
||||
{
|
||||
_alpha.animate();
|
||||
_r.animate();
|
||||
_g.animate();
|
||||
_b.animate();
|
||||
|
||||
_for_each_element([&] (Element &element) { element.animate(); });
|
||||
|
||||
Animator::Item::animated(animated());
|
||||
|
||||
_repaint_decorations(*_buffer_top_bottom);
|
||||
_repaint_decorations(*_buffer_left_right);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _WINDOW_H_ */
|
Loading…
Reference in New Issue
Block a user