Serenity Operating System
at hosted 231 lines 6.9 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/JsonObject.h> 28#include <LibCore/Timer.h> 29#include <LibGUI/AbstractButton.h> 30#include <LibGUI/Painter.h> 31#include <LibGfx/Palette.h> 32 33namespace GUI { 34 35AbstractButton::AbstractButton(const StringView& text) 36 : m_text(text) 37{ 38 m_auto_repeat_timer = add<Core::Timer>(); 39 m_auto_repeat_timer->on_timeout = [this] { 40 click(); 41 }; 42} 43 44AbstractButton::~AbstractButton() 45{ 46} 47 48void AbstractButton::set_text(const StringView& text) 49{ 50 if (m_text == text) 51 return; 52 m_text = text; 53 update(); 54} 55 56void AbstractButton::set_checked(bool checked) 57{ 58 if (m_checked == checked) 59 return; 60 m_checked = checked; 61 62 if (is_exclusive() && checked) { 63 parent_widget()->for_each_child_of_type<AbstractButton>([&](auto& sibling) { 64 if (!sibling.is_exclusive() || !sibling.is_checked()) 65 return IterationDecision::Continue; 66 sibling.m_checked = false; 67 sibling.update(); 68 if (sibling.on_checked) 69 sibling.on_checked(false); 70 return IterationDecision::Continue; 71 }); 72 m_checked = true; 73 } 74 75 update(); 76 if (on_checked) 77 on_checked(checked); 78} 79 80void AbstractButton::set_checkable(bool checkable) 81{ 82 if (m_checkable == checkable) 83 return; 84 m_checkable = checkable; 85 update(); 86} 87 88void AbstractButton::mousemove_event(MouseEvent& event) 89{ 90 bool is_over = rect().contains(event.position()); 91 m_hovered = is_over; 92 if (event.buttons() & MouseButton::Left) { 93 if (is_enabled()) { 94 bool being_pressed = is_over; 95 if (being_pressed != m_being_pressed) { 96 m_being_pressed = being_pressed; 97 if (m_auto_repeat_interval) { 98 if (!m_being_pressed) 99 m_auto_repeat_timer->stop(); 100 else 101 m_auto_repeat_timer->start(m_auto_repeat_interval); 102 } 103 update(); 104 } 105 } 106 } 107 Widget::mousemove_event(event); 108} 109 110void AbstractButton::mousedown_event(MouseEvent& event) 111{ 112#ifdef GABSTRACTBUTTON_DEBUG 113 dbgprintf("AbstractButton::mouse_down_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); 114#endif 115 if (event.button() == MouseButton::Left) { 116 if (is_enabled()) { 117 m_being_pressed = true; 118 update(); 119 120 if (m_auto_repeat_interval) { 121 click(); 122 m_auto_repeat_timer->start(m_auto_repeat_interval); 123 } 124 } 125 } 126 Widget::mousedown_event(event); 127} 128 129void AbstractButton::mouseup_event(MouseEvent& event) 130{ 131#ifdef GABSTRACTBUTTON_DEBUG 132 dbgprintf("AbstractButton::mouse_up_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); 133#endif 134 if (event.button() == MouseButton::Left) { 135 bool was_auto_repeating = m_auto_repeat_timer->is_active(); 136 m_auto_repeat_timer->stop(); 137 if (is_enabled()) { 138 bool was_being_pressed = m_being_pressed; 139 m_being_pressed = false; 140 update(); 141 if (was_being_pressed && !was_auto_repeating) 142 click(); 143 } 144 } 145 Widget::mouseup_event(event); 146} 147 148void AbstractButton::enter_event(Core::Event&) 149{ 150 m_hovered = true; 151 update(); 152} 153 154void AbstractButton::leave_event(Core::Event&) 155{ 156 m_hovered = false; 157 update(); 158} 159 160void AbstractButton::keydown_event(KeyEvent& event) 161{ 162 if (event.key() == KeyCode::Key_Return) { 163 click(); 164 event.accept(); 165 return; 166 } 167 Widget::keydown_event(event); 168} 169 170void AbstractButton::paint_text(Painter& painter, const Gfx::Rect& rect, const Gfx::Font& font, Gfx::TextAlignment text_alignment) 171{ 172 auto clipped_rect = rect.intersected(this->rect()); 173 174 if (!is_enabled()) { 175 painter.draw_text(clipped_rect.translated(1, 1), text(), font, text_alignment, Color::White, Gfx::TextElision::Right); 176 painter.draw_text(clipped_rect, text(), font, text_alignment, Color::from_rgb(0x808080), Gfx::TextElision::Right); 177 return; 178 } 179 180 if (text().is_empty()) 181 return; 182 painter.draw_text(clipped_rect, text(), font, text_alignment, palette().button_text(), Gfx::TextElision::Right); 183 if (is_focused()) 184 painter.draw_rect(clipped_rect.inflated(6, 4), palette().focus_outline()); 185} 186 187void AbstractButton::change_event(Event& event) 188{ 189 if (event.type() == Event::Type::EnabledChange) { 190 if (!is_enabled()) { 191 bool was_being_pressed = m_being_pressed; 192 m_being_pressed = false; 193 if (was_being_pressed) 194 update(); 195 } 196 } 197 Widget::change_event(event); 198} 199 200void AbstractButton::save_to(JsonObject& json) 201{ 202 json.set("text", m_text); 203 json.set("checked", m_checked); 204 json.set("checkable", m_checkable); 205 json.set("exclusive", m_exclusive); 206 Widget::save_to(json); 207} 208 209bool AbstractButton::set_property(const StringView& name, const JsonValue& value) 210{ 211 if (name == "text") { 212 set_text(value.to_string()); 213 return true; 214 } 215 if (name == "checked") { 216 set_checked(value.to_bool()); 217 return true; 218 } 219 if (name == "checkable") { 220 set_checkable(value.to_bool()); 221 return true; 222 } 223 if (name == "exclusive") { 224 set_exclusive(value.to_bool()); 225 return true; 226 } 227 228 return Widget::set_property(name, value); 229} 230 231}