diff --git a/repos/gems/src/app/decorator/window.cc b/repos/gems/src/app/decorator/window.cc index 2ee1a85eaf..59cb643976 100644 --- a/repos/gems/src/app/decorator/window.cc +++ b/repos/gems/src/app/decorator/window.cc @@ -31,35 +31,35 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, draw_behind_fn.draw_behind(canvas, *this, canvas.clip()); _draw_corner(canvas, Rect(p1, corner), _border_size, true, true, - element(Element::TOP_LEFT).color()); + _window_elem_attr(Element::TOP_LEFT)); _draw_corner(canvas, Rect(Point(p1.x(), p2.y() - _corner_size + 1), corner), _border_size, true, false, - element(Element::BOTTOM_LEFT).color()); + _window_elem_attr(Element::BOTTOM_LEFT)); _draw_corner(canvas, Rect(Point(p2.x() - _corner_size + 1, p1.y()), corner), _border_size, false, true, - element(Element::TOP_RIGHT).color()); + _window_elem_attr(Element::TOP_RIGHT)); _draw_corner(canvas, Rect(Point(p2.x() - _corner_size + 1, p2.y() - _corner_size + 1), corner), _border_size, false, false, - element(Element::BOTTOM_RIGHT).color()); + _window_elem_attr(Element::BOTTOM_RIGHT)); _draw_raised_box(canvas, Rect(Point(p1.x() + _corner_size, p1.y()), Area(rect.w() - 2*_corner_size, _border_size)), - element(Element::TOP).color()); + _window_elem_attr(Element::TOP)); _draw_raised_box(canvas, Rect(Point(p1.x() + _corner_size, p2.y() - _border_size + 1), Area(rect.w() - 2*_corner_size, _border_size)), - element(Element::BOTTOM).color()); + _window_elem_attr(Element::BOTTOM)); _draw_raised_box(canvas, Rect(Point(p1.x(), p1.y() + _corner_size), Area(_border_size, rect.h() - 2*_corner_size)), - element(Element::LEFT).color()); + _window_elem_attr(Element::LEFT)); _draw_raised_box(canvas, Rect(Point(p2.x() - _border_size + 1, p1.y() + _corner_size), Area(_border_size, rect.h() - 2*_corner_size)), - element(Element::RIGHT).color()); + _window_elem_attr(Element::RIGHT)); Rect controls_rect(Point(p1.x() + _border_size, p1.y() + _border_size), Area(rect.w() - 2*_border_size, _title_height)); @@ -118,7 +118,7 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, Rect title_rect(left_pos, Area(right_pos.x() - left_pos.x() + _icon_size.w(), _title_height)); - _draw_title_box(canvas, title_rect, element(Element::TITLE).color()); + _draw_title_box(canvas, title_rect, _window_elem_attr(Element::TITLE)); char const * const text = _title.string(); @@ -128,7 +128,9 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, /* * Position the text in the center of the window. */ - Point const window_centered_text_pos = controls_rect.center(label_area) - Point(0, 1); + int const title_yshift = element(Element::TITLE).pressed() ? 0 : -1; + Point const window_centered_text_pos = controls_rect.center(label_area) + + Point(0, title_yshift); /* * Horizontal position of the title text @@ -259,17 +261,31 @@ bool Decorator::Window::update(Genode::Xml_node window_node) updated |= (new_controls != _controls); _controls = new_controls; - try { - Xml_node highlight = window_node.sub_node("highlight"); + Xml_node const highlight = window_node.has_sub_node("highlight") + ? window_node.sub_node("highlight") + : Xml_node(""); - for (unsigned i = 0; i < num_elements(); i++) - updated |= _apply_state(_elements[i].type(), - highlight.has_sub_node(_elements[i].type_name())); - } catch (...) { + for (unsigned i = 0; i < num_elements(); i++) { - /* window node has no "highlight" sub node, reset highlighting */ - for (unsigned i = 0; i < num_elements(); i++) - updated |= _apply_state(_elements[i].type(), false); + Window_element &element = _elements[i]; + + Window_element::State state = element.state(); + + state.highlighted = false; + state.pressed = false; + state.focused = _focused; + + highlight.for_each_sub_node([&] (Xml_node node) { + + if (node.type() == element.type_name()) { + state.highlighted = true; + + if (node.attribute_value("pressed", false)) + state.pressed = true; + } + }); + + updated |= element.apply_state(state); } return updated; diff --git a/repos/gems/src/app/decorator/window.h b/repos/gems/src/app/decorator/window.h index 991b254405..4a73645db8 100644 --- a/repos/gems/src/app/decorator/window.h +++ b/repos/gems/src/app/decorator/window.h @@ -112,8 +112,8 @@ class Decorator::Window : public Window_base static unsigned const _border_size = 4; static unsigned const _title_height = 16; - Color _bright = { 255, 255, 255, 64 }; + Color _dimmed = { 0, 0, 0, 24 }; Color _dark = { 0, 0, 0, 127 }; Color _base_color = _config.base_color(_title); @@ -161,9 +161,10 @@ class Decorator::Window : public Window_base unsigned num_elements() const { return sizeof(_elements)/sizeof(Element); } - bool _apply_state(Window::Element::Type type, bool highlighted) + bool _apply_state(Window::Element::Type type, + Window::Element::State const &state) { - return element(type).apply_state(_focused, highlighted, _base_color); + return element(type).apply_state(state); } typedef Config::Window_control Control; @@ -225,6 +226,12 @@ class Decorator::Window : public Window_base ** Drawing utilities ** ***********************/ + struct Attr + { + Color color; + bool pressed; + }; + void _draw_hline(Canvas_base &canvas, Point pos, unsigned w, bool at_left, bool at_right, unsigned border, Color color) const @@ -247,20 +254,23 @@ class Decorator::Window : public Window_base Point(pos.x(), y2)), color); } - void _draw_raised_frame(Canvas_base &canvas, Rect rect) const + void _draw_raised_frame(Canvas_base &canvas, Rect rect, bool pressed) const { - _draw_hline(canvas, rect.p1(), rect.w(), true, true, 0, _bright); - _draw_vline(canvas, rect.p1(), rect.h(), true, true, 0, _bright); + Color const top_left_color = pressed ? _dimmed : _bright; + + _draw_hline(canvas, rect.p1(), rect.w(), true, true, 0, top_left_color); + _draw_vline(canvas, rect.p1(), rect.h(), true, true, 0, top_left_color); + _draw_hline(canvas, Point(rect.p1().x(), rect.p2().y()), rect.w(), true, true, 0, _dark); _draw_vline(canvas, Point(rect.p2().x(), rect.p1().y()), rect.h(), true, true, 0, _dark); } - void _draw_raised_box(Canvas_base &canvas, Rect rect, Color color) const + void _draw_raised_box(Canvas_base &canvas, Rect rect, Attr attr) const { - canvas.draw_box(rect, color); - _draw_raised_frame(canvas, rect); + canvas.draw_box(rect, attr.color); + _draw_raised_frame(canvas, rect, attr.pressed); } static Color _mix_colors(Color c1, Color c2, int alpha) @@ -270,7 +280,7 @@ class Decorator::Window : public Window_base (c1.b*alpha + c2.b*(255 - alpha)) >> 8); } - void _draw_title_box(Canvas_base &canvas, Rect rect, Color color) const + void _draw_title_box(Canvas_base &canvas, Rect rect, Attr attr) const { /* * Produce gradient such that the upper half becomes brighter and @@ -284,7 +294,8 @@ class Decorator::Window : public Window_base int const mid_y = rect.h() / 2; - Color const white(255, 255, 255), black(0, 0, 0); + Color const upper_color = attr.pressed ? Color(0, 0, 0) : Color(255, 255, 255); + Color const lower_color = attr.pressed ? Color(127, 127, 127) : Color(0, 0, 0); for (unsigned i = 0; i < rect.h(); i++) { @@ -294,20 +305,19 @@ class Decorator::Window : public Window_base ? (ascent*(mid_y - i)) >> 8 : (ascent*(i - mid_y)) >> 8; - Color const line_color = - _mix_colors(upper_half ? white : black, color, alpha); + Color const mix_color = upper_half ? upper_color : lower_color; + Color const line_color = _mix_colors(mix_color, attr.color, alpha); canvas.draw_box(Rect(rect.p1() + Point(0, i), Area(rect.w(), 1)), line_color); } - _draw_raised_frame(canvas, rect); + _draw_raised_frame(canvas, rect, attr.pressed); } void _draw_corner(Canvas_base &canvas, Rect const rect, unsigned const border, - bool const left, bool const top, - Color color) const + bool const left, bool const top, Attr const attr) const { bool const bottom = !top; bool const right = !left; @@ -319,21 +329,23 @@ class Decorator::Window : public Window_base int const w = rect.w(); int const h = rect.h(); + Color const top_left_color = attr.pressed ? _dimmed : _bright; + canvas.draw_box(Rect(Point(x1, top ? y1 : y2 - border + 1), - Area(w, border)), color); + Area(w, border)), attr.color); canvas.draw_box(Rect(Point(left ? x1 : x2 - border + 1, top ? y1 + border : y1), - Area(border, h - border)), color); + Area(border, h - border)), attr.color); /* top bright line */ _draw_hline(canvas, rect.p1(), w, - top || left, top || right, border, _bright); + top || left, top || right, border, top_left_color); /* inner horizontal line */ int y = top ? y1 + border - 1 : y2 - border + 1; _draw_hline(canvas, Point(x1, y), w, right, left, w - border, - top ? _dark : _bright); + top ? _dark : top_left_color); /* bottom line */ _draw_hline(canvas, Point(x1, y2), w, @@ -341,29 +353,35 @@ class Decorator::Window : public Window_base /* left bright line */ _draw_vline(canvas, rect.p1(), h, - left || top, left || bottom, border, _bright); + left || top, left || bottom, border, top_left_color); /* inner vertical line */ int x = left ? x1 + border - 1 : x2 - border + 1; _draw_vline(canvas, Point(x, y1), h, bottom, top, h - border + 1, - left ? _dark : _bright); + left ? _dark : top_left_color); /* right line */ _draw_vline(canvas, Point(x2, y1), h, right || top, right || bottom, border, _dark); } - Color _window_control_color(Control window_control) const + Attr _window_elem_attr(Element::Type type) const + { + return Attr { .color = element(type).color(), + .pressed = element(type).pressed() }; + } + + Attr _window_control_attr(Control const &window_control) const { switch (window_control.type()) { - case Control::TYPE_CLOSER: return element(Element::CLOSER).color(); - case Control::TYPE_MAXIMIZER: return element(Element::MAXIMIZER).color(); - case Control::TYPE_MINIMIZER: return element(Element::MINIMIZER).color(); - case Control::TYPE_UNMAXIMIZER: return element(Element::UNMAXIMIZER).color(); - case Control::TYPE_TITLE: return element(Element::TITLE).color(); + case Control::TYPE_CLOSER: return _window_elem_attr(Element::CLOSER); + case Control::TYPE_MAXIMIZER: return _window_elem_attr(Element::MAXIMIZER); + case Control::TYPE_MINIMIZER: return _window_elem_attr(Element::MINIMIZER); + case Control::TYPE_UNMAXIMIZER: return _window_elem_attr(Element::UNMAXIMIZER); + case Control::TYPE_TITLE: return _window_elem_attr(Element::TITLE); case Control::TYPE_UNDEFINED: break; }; - return Color(0, 0, 0); + return Attr { .color = Color(0, 0, 0), .pressed = false }; } Texture_id _window_control_texture(Control window_control) const @@ -385,7 +403,7 @@ class Decorator::Window : public Window_base void _draw_window_control(Canvas_base &canvas, Rect rect, Control control) const { - _draw_title_box(canvas, rect, _window_control_color(control)); + _draw_title_box(canvas, rect, _window_control_attr(control)); canvas.draw_texture(rect.p1() + Point(1,1), _window_control_texture(control)); diff --git a/repos/gems/src/app/decorator/window_element.h b/repos/gems/src/app/decorator/window_element.h index 8a6ab0b716..a4826e918c 100644 --- a/repos/gems/src/app/decorator/window_element.h +++ b/repos/gems/src/app/decorator/window_element.h @@ -35,6 +35,22 @@ class Decorator::Window_element : public Animator::Item TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CLOSER, MAXIMIZER, MINIMIZER, UNMAXIMIZER, UNDEFINED }; + struct State + { + bool focused; + bool highlighted; + bool pressed; + Color base_color; + + bool operator == (State const &other) const + { + return focused == other.focused + && highlighted == other.highlighted + && pressed == other.pressed + && base_color == other.base_color; + } + }; + private: static Color _add(Color c1, Color c2) @@ -44,12 +60,16 @@ class Decorator::Window_element : public Animator::Item Genode::min(c1.b + c2.b, 255)); } + static Color _sub(Color c1, Color c2) + { + return Color(Genode::max(c1.r - c2.r, 0), + Genode::max(c1.g - c2.g, 0), + Genode::max(c1.b - c2.b, 0)); + } + Type const _type; - /* - * Rememeber base color to detect when it changes - */ - Color _base_color { }; + State _state { }; /* * Color value in 8.4 fixpoint format. We use four bits to @@ -58,52 +78,37 @@ class Decorator::Window_element : public Animator::Item */ Lazy_value _r { }, _g { }, _b { }; - bool _focused = false; - bool _highlighted = false; - - static Color _dst_color(bool focused, bool highlighted, Color base) + static Color _dst_color(State const &state) { - Color result = base; + Color result = state.base_color; - if (focused) + if (state.focused) result = _add(result, Color(70, 70, 70)); - if (highlighted) + if (state.highlighted) result = _add(result, Color(65, 60, 55)); + if (state.pressed) + result = _sub(result, Color(10, 10, 10)); + return result; } - unsigned _anim_steps(bool focused, bool highlighted) const + unsigned _anim_steps(State const &state) const { - /* quick fade-in when gaining the focus or hover highlight */ - if ((!_focused && focused) || (!_highlighted && highlighted)) + /* immediately respond when pressing or releasing an element */ + if (_state.pressed != state.pressed) + return 0; + + /* medium fade-in when gaining the focus or hover highlight */ + if ((!_state.focused && state.focused) + || (!_state.highlighted && state.highlighted)) return 15; /* slow fade-out when leaving focus or hover highlight */ return 20; } - bool _apply_state(bool focused, bool highlighted, Color base_color) - { - _base_color = base_color; - - Color const dst_color = _dst_color(focused, highlighted, base_color); - unsigned const steps = _anim_steps(focused, highlighted); - - _r.dst(dst_color.r << 4, steps); - _g.dst(dst_color.g << 4, steps); - _b.dst(dst_color.b << 4, steps); - - /* schedule animation */ - animate(); - - _focused = focused; - _highlighted = highlighted; - - return true; - } - public: Window_element(Type type, Animator &animator, Color base_color) @@ -111,7 +116,10 @@ class Decorator::Window_element : public Animator::Item Animator::Item(animator), _type(type) { - _apply_state(false, false, base_color); + apply_state(State{ .focused = false, + .highlighted = false, + .pressed = false, + .base_color = base_color }); } Type type() const { return _type; } @@ -142,15 +150,30 @@ class Decorator::Window_element : public Animator::Item /** * \return true if state has changed */ - bool apply_state(bool focused, bool highlighted, Color base_color) + bool apply_state(State state) { - if (_focused == focused && _highlighted == highlighted - && base_color == _base_color) + if (_state == state) return false; - return _apply_state(focused, highlighted, base_color); + Color const dst_color = _dst_color(state); + unsigned const steps = _anim_steps(state); + + _r.dst(dst_color.r << 4, steps); + _g.dst(dst_color.g << 4, steps); + _b.dst(dst_color.b << 4, steps); + + /* schedule animation */ + animate(); + + _state = state; + + return true; } + State state() const { return _state; } + + bool pressed() const { return _state.pressed; } + /** * Animator::Item interface */