Serenity Operating System
at master 136 lines 4.3 kB view raw
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}