Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibGfx/CharacterBitmap.h>
8#include <LibGfx/Painter.h>
9#include <LibGfx/StylePainter.h>
10#include <WindowServer/Button.h>
11#include <WindowServer/Event.h>
12#include <WindowServer/Screen.h>
13#include <WindowServer/WindowManager.h>
14
15namespace WindowServer {
16
17Button::Button(WindowFrame& frame, Function<void(Button&)>&& on_click_handler)
18 : on_click(move(on_click_handler))
19 , m_frame(frame)
20{
21}
22
23Button::~Button() = default;
24
25void Button::paint(Screen& screen, Gfx::Painter& painter)
26{
27 auto palette = WindowManager::the().palette();
28 Gfx::PainterStateSaver saver(painter);
29 painter.translate(relative_rect().location());
30
31 if (m_style == Style::Normal)
32 Gfx::StylePainter::paint_button(painter, rect(), palette, Gfx::ButtonStyle::Normal, m_pressed, m_hovered);
33
34 auto paint_icon = [&](auto& multiscale_bitmap) {
35 auto& bitmap = multiscale_bitmap->bitmap(screen.scale_factor());
36 auto icon_location = rect().center().translated(-(bitmap.width() / 2), -(bitmap.height() / 2));
37 if (m_pressed)
38 painter.translate(1, 1);
39 painter.blit(icon_location, bitmap, bitmap.rect());
40 };
41
42 if (m_hovered && m_icon.hover_bitmap && !m_icon.hover_bitmap->is_empty())
43 paint_icon(m_icon.hover_bitmap);
44 else if (m_icon.bitmap)
45 paint_icon(m_icon.bitmap);
46}
47
48void Button::on_mouse_event(MouseEvent const& event)
49{
50 auto interesting_button = false;
51
52 switch (event.button()) {
53 case MouseButton::Primary:
54 interesting_button = !!on_click;
55 break;
56 case MouseButton::Middle:
57 interesting_button = !!on_middle_click;
58 break;
59 case MouseButton::Secondary:
60 interesting_button = !!on_secondary_click;
61 break;
62 default:
63 break;
64 }
65
66 if (event.type() != Event::Type::MouseMove && !interesting_button)
67 return;
68
69 auto& wm = WindowManager::the();
70
71 if (event.type() == Event::MouseDown && (event.button() == MouseButton::Primary || event.button() == MouseButton::Secondary || event.button() == MouseButton::Middle)) {
72 m_pressed = true;
73 wm.set_cursor_tracking_button(this);
74 m_frame.invalidate(m_relative_rect);
75 return;
76 }
77
78 if (event.type() == Event::MouseUp && (event.button() == MouseButton::Primary || event.button() == MouseButton::Secondary || event.button() == MouseButton::Middle)) {
79 if (wm.cursor_tracking_button() != this)
80 return;
81 wm.set_cursor_tracking_button(nullptr);
82 bool old_pressed = m_pressed;
83 m_pressed = false;
84 if (rect().contains(event.position())) {
85 switch (event.button()) {
86 case MouseButton::Primary:
87 if (on_click)
88 on_click(*this);
89 break;
90
91 case MouseButton::Secondary:
92 if (on_secondary_click)
93 on_secondary_click(*this);
94 break;
95
96 default:
97 if (on_middle_click)
98 on_middle_click(*this);
99 break;
100 }
101 }
102 if (old_pressed != m_pressed) {
103 // Would like to compute:
104 // m_hovered = rect_after_action().contains(event.position());
105 // However, we don't know that rect yet. We can make an educated
106 // guess which also looks okay even when wrong:
107 m_hovered = false;
108 m_frame.invalidate(m_relative_rect);
109 }
110 return;
111 }
112
113 if (event.type() == Event::MouseMove) {
114 bool old_hovered = m_hovered;
115 m_hovered = rect().contains(event.position());
116 wm.set_hovered_button(m_hovered ? this : nullptr);
117 if (old_hovered != m_hovered)
118 m_frame.invalidate(m_relative_rect);
119 }
120
121 if (event.type() == Event::MouseMove && event.buttons() & (unsigned)MouseButton::Primary) {
122 if (wm.cursor_tracking_button() != this)
123 return;
124 bool old_pressed = m_pressed;
125 m_pressed = m_hovered;
126 if (old_pressed != m_pressed)
127 m_frame.invalidate(m_relative_rect);
128 }
129}
130
131Gfx::IntRect Button::screen_rect() const
132{
133 return m_relative_rect.translated(m_frame.rect().location());
134}
135
136}