From 35c06f15202c580199e71019f215d8b065e44239 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 27 Jan 2019 20:22:06 +0100 Subject: [PATCH] LibGUI: More work on GCheckBox. - Make it track the mouse cursor just like GButton does so that changes only get committed if the mouseup event happens while inside the widget rect. - Draw a focus rect around the box when appropriate. - When focused, support toggling the checked state with the space bar. --- LibGUI/GCheckBox.cpp | 160 ++++++++++++++++++++++++++---------------- LibGUI/GCheckBox.h | 6 +- Userland/guitest2.cpp | 5 ++ 3 files changed, 111 insertions(+), 60 deletions(-) diff --git a/LibGUI/GCheckBox.cpp b/LibGUI/GCheckBox.cpp index 31d8361df13..ad973d94f2f 100644 --- a/LibGUI/GCheckBox.cpp +++ b/LibGUI/GCheckBox.cpp @@ -1,10 +1,33 @@ #include "GCheckBox.h" #include #include +#include + +//#define GCHECKBOX_DEBUG + +static const char* s_checked_bitmap_data = { + " " + " ## " + " ## " + " ## " + " ## " + " ## ## " + " #### " + " ## " + " " +}; + +static CharacterBitmap* s_checked_bitmap; +static const int s_checked_bitmap_width = 9; +static const int s_checked_bitmap_height = 9; +static const int s_box_width = 11; +static const int s_box_height = 11; GCheckBox::GCheckBox(GWidget* parent) : GWidget(parent) { + if (!s_checked_bitmap) + s_checked_bitmap = CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leakRef(); } GCheckBox::~GCheckBox() @@ -27,76 +50,95 @@ void GCheckBox::set_checked(bool b) update(); } -static const char* uncheckedBitmap = { - "###########" - "# #" - "# #" - "# #" - "# #" - "# #" - "# #" - "# #" - "# #" - "# #" - "###########" -}; - -#if 0 -static const char* checkedBitmap = { - "############" - "# #" - "# ## #" - "# ## #" - "# ## #" - "# ## #" - "# ## #" - "# ## ## #" - "# ## ## #" - "# ### #" - "# #" - "############" -}; -#endif - -static const char* checkedBitmap = { - "###########" - "## ##" - "# # # #" - "# # # #" - "# # # #" - "# # #" - "# # # #" - "# # # #" - "# # # #" - "## ##" - "###########" -}; - void GCheckBox::paint_event(GPaintEvent&) { Painter painter(*this); - auto bitmap = CharacterBitmap::create_from_ascii(is_checked() ? checkedBitmap : uncheckedBitmap, 11, 11); - auto textRect = rect(); - textRect.set_left(bitmap->width() + 4); - textRect.set_top(height() / 2 - font().glyph_height() / 2); + auto text_rect = rect(); + text_rect.set_left(s_box_width + 4); + text_rect.set_top(height() / 2 - font().glyph_height() / 2); - Point bitmapPosition; - bitmapPosition.set_x(2); - bitmapPosition.set_y(height() / 2 - bitmap->height() / 2 - 1); + if (fill_with_background_color()) + painter.fill_rect(rect(), background_color()); - painter.fill_rect(rect(), background_color()); - painter.draw_bitmap(bitmapPosition, *bitmap, foreground_color()); + Rect box_rect { + 2, height() / 2 - s_box_height / 2 - 1, + s_box_width, s_box_height + }; + painter.draw_rect(box_rect, Color::Black); - if (!caption().is_empty()) { - painter.draw_text(textRect, caption(), Painter::TextAlignment::TopLeft, foreground_color()); + if (m_being_modified) { + auto modification_rect = box_rect; + modification_rect.shrink(2, 2); + painter.draw_rect(modification_rect, Color::MidGray); } + + if (m_checked) { + auto bitmap_rect = box_rect; + bitmap_rect.shrink(2, 2); + painter.draw_bitmap(bitmap_rect.location(), *s_checked_bitmap, foreground_color()); + } + + if (!caption().is_empty()) + painter.draw_text(text_rect, caption(), Painter::TextAlignment::TopLeft, foreground_color()); + + if (is_focused()) { + // NOTE: Painter::draw_focus_rect() will shrink(2,2) the passed rect. + auto focus_rect = box_rect; + focus_rect.inflate(4, 4); + painter.draw_focus_rect(focus_rect); + } +} + +void GCheckBox::mousemove_event(GMouseEvent& event) +{ + if (m_tracking_cursor) { + bool being_pressed = rect().contains(event.position()); + if (being_pressed != m_being_modified) { + m_being_modified = being_pressed; + update(); + } + } + GWidget::mousemove_event(event); } void GCheckBox::mousedown_event(GMouseEvent& event) { - dbgprintf("GCheckBox::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); - - set_checked(!is_checked()); +#ifdef GCHECKBOX_DEBUG + dbgprintf("GCheckBox::mouse_down_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); +#endif + if (event.button() == GMouseButton::Left) { + m_being_modified = true; + m_tracking_cursor = true; + set_global_cursor_tracking(true); + update(); + } + GWidget::mousedown_event(event); } +void GCheckBox::mouseup_event(GMouseEvent& event) +{ +#ifdef GCHECKBOX_DEBUG + dbgprintf("GCheckBox::mouseup_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); +#endif + if (event.button() == GMouseButton::Left) { + bool was_being_pressed = m_being_modified; + m_being_modified = false; + m_tracking_cursor = false; + set_global_cursor_tracking(false); + if (was_being_pressed) { + set_checked(!is_checked()); + } + update(); + } + GWidget::mouseup_event(event); +} + +void GCheckBox::keydown_event(GKeyEvent& event) +{ + if (!m_tracking_cursor && event.key() == KeyCode::Key_Space) { + set_checked(!is_checked()); + update(); + } + GWidget::keydown_event(event); +} diff --git a/LibGUI/GCheckBox.h b/LibGUI/GCheckBox.h index cee0d1bc1e2..4d3ddde3733 100644 --- a/LibGUI/GCheckBox.h +++ b/LibGUI/GCheckBox.h @@ -17,11 +17,15 @@ public: private: virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; - + virtual void mouseup_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; + virtual void keydown_event(GKeyEvent&) override; virtual const char* class_name() const override { return "GCheckBox"; } virtual bool accepts_focus() const override { return true; } String m_caption; bool m_checked { false }; + bool m_being_modified { false }; + bool m_tracking_cursor { false }; }; diff --git a/Userland/guitest2.cpp b/Userland/guitest2.cpp index 541266cfecb..6d23d8bb979 100644 --- a/Userland/guitest2.cpp +++ b/Userland/guitest2.cpp @@ -14,6 +14,7 @@ #include #include #include +#include static GWindow* make_font_test_window(); static GWindow* make_launcher_window(); @@ -117,6 +118,10 @@ GWindow* make_launcher_window() auto* other_textbox = new GTextBox(widget); other_textbox->set_relative_rect({ 5, 140, 90, 20 }); + auto* checkbox = new GCheckBox(widget); + checkbox->set_relative_rect({ 5, 170, 90, 20 }); + checkbox->set_caption("CheckBox"); + window->set_focused_widget(textbox); return window;