summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2018-10-12 14:15:14 +0200
committerAndreas Kling <awesomekling@gmail.com>2018-10-12 14:16:40 +0200
commit16576112b0004a912f73f216488ebfda50ed1efe (patch)
tree0a2da73508c4c137609abaeb8508fd54f8b71c0a
parentc7463aad1159488fc6b9f7134dd88d0bf0b87b27 (diff)
downloadserenity-16576112b0004a912f73f216488ebfda50ed1efe.zip
Add a CheckBox widget.
-rw-r--r--Widgets/Button.cpp4
-rw-r--r--Widgets/CheckBox.cpp84
-rw-r--r--Widgets/CheckBox.h26
-rw-r--r--Widgets/ClockWidget.cpp2
-rw-r--r--Widgets/Makefile1
-rw-r--r--Widgets/Painter.cpp8
-rw-r--r--Widgets/Rect.h12
-rw-r--r--Widgets/TerminalWidget.cpp2
-rw-r--r--Widgets/Widget.cpp8
-rw-r--r--Widgets/Widget.h18
-rw-r--r--Widgets/Window.cpp9
-rw-r--r--Widgets/WindowManager.cpp6
-rw-r--r--Widgets/test.cpp33
13 files changed, 177 insertions, 36 deletions
diff --git a/Widgets/Button.cpp b/Widgets/Button.cpp
index f42977dafb..b5648252f0 100644
--- a/Widgets/Button.cpp
+++ b/Widgets/Button.cpp
@@ -22,9 +22,9 @@ void Button::setCaption(String&& caption)
void Button::onPaint(PaintEvent&)
{
Painter painter(*this);
- painter.fillRect({ 0, 0, width(), height() }, backgroundColor());
+ painter.fillRect(rect(), backgroundColor());
if (!caption().isEmpty()) {
- painter.drawText({ 0, 0, width(), height() }, caption(), Painter::TextAlignment::Center, Color(0, 0, 0));
+ painter.drawText(rect(), caption(), Painter::TextAlignment::Center, Color(0, 0, 0));
}
}
diff --git a/Widgets/CheckBox.cpp b/Widgets/CheckBox.cpp
new file mode 100644
index 0000000000..f9ee65e333
--- /dev/null
+++ b/Widgets/CheckBox.cpp
@@ -0,0 +1,84 @@
+#include "CheckBox.h"
+#include "Painter.h"
+#include "CBitmap.h"
+#include <cstdio>
+
+CheckBox::CheckBox(Widget* parent)
+ : Widget(parent)
+{
+}
+
+CheckBox::~CheckBox()
+{
+}
+
+void CheckBox::setCaption(String&& caption)
+{
+ if (caption == m_caption)
+ return;
+ m_caption = std::move(caption);
+ update();
+}
+
+void CheckBox::setIsChecked(bool b)
+{
+ if (m_isChecked == b)
+ return;
+ m_isChecked = b;
+ update();
+}
+
+static const char* uncheckedBitmap = {
+ "############"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "# #"
+ "############"
+};
+
+static const char* checkedBitmap = {
+ "############"
+ "# #"
+ "# # # #"
+ "# # # #"
+ "# # # #"
+ "# ## #"
+ "# ## #"
+ "# # # #"
+ "# # # #"
+ "# # # #"
+ "# #"
+ "############"
+};
+
+void CheckBox::onPaint(PaintEvent&)
+{
+ Painter painter(*this);
+ auto bitmap = CBitmap::createFromASCII(isChecked() ? checkedBitmap : uncheckedBitmap, 12, 12);
+
+ auto textRect = rect();
+ textRect.setLeft(bitmap->width() + 4);
+ textRect.setTop(height() / 2 - bitmap->height() / 2);
+
+ painter.fillRect(rect(), backgroundColor());
+ painter.drawBitmap({ 2, textRect.y() }, *bitmap, Color(0, 0, 0));
+
+ if (!caption().isEmpty()) {
+ painter.drawText(textRect, caption(), Painter::TextAlignment::TopLeft, Color(0, 0, 0));
+ }
+}
+
+void CheckBox::onMouseDown(MouseEvent& event)
+{
+ printf("CheckBox::onMouseDown: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
+
+ setIsChecked(!isChecked());
+}
+
diff --git a/Widgets/CheckBox.h b/Widgets/CheckBox.h
new file mode 100644
index 0000000000..f653aa803d
--- /dev/null
+++ b/Widgets/CheckBox.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Widget.h"
+#include <AK/String.h>
+
+class CheckBox final : public Widget {
+public:
+ explicit CheckBox(Widget* parent);
+ virtual ~CheckBox() override;
+
+ String caption() const { return m_caption; }
+ void setCaption(String&&);
+
+ bool isChecked() const { return m_isChecked; }
+ void setIsChecked(bool);
+
+private:
+ virtual void onPaint(PaintEvent&) override;
+ virtual void onMouseDown(MouseEvent&) override;
+
+ virtual const char* className() const override { return "CheckBox"; }
+
+ String m_caption;
+ bool m_isChecked { false };
+};
+
diff --git a/Widgets/ClockWidget.cpp b/Widgets/ClockWidget.cpp
index a4778e2ca2..a0e2aadcaa 100644
--- a/Widgets/ClockWidget.cpp
+++ b/Widgets/ClockWidget.cpp
@@ -5,7 +5,7 @@
ClockWidget::ClockWidget(Widget* parent)
: Widget(parent)
{
- setRect({ 0, 0, 100, 40 });
+ setWindowRelativeRect({ 0, 0, 100, 40 });
startTimer(250);
}
diff --git a/Widgets/Makefile b/Widgets/Makefile
index 2754956941..2777bed7e1 100644
--- a/Widgets/Makefile
+++ b/Widgets/Makefile
@@ -24,6 +24,7 @@ VFS_OBJS = \
Window.o \
ClockWidget.o \
CBitmap.o \
+ CheckBox.o \
test.o
OBJS = $(AK_OBJS) $(VFS_OBJS)
diff --git a/Widgets/Painter.cpp b/Widgets/Painter.cpp
index 03cd713a7f..c95bb5cdaa 100644
--- a/Widgets/Painter.cpp
+++ b/Widgets/Painter.cpp
@@ -12,7 +12,7 @@ Painter::Painter(Widget& widget)
{
if (auto* window = widget.window()) {
m_translation = window->position();
- m_translation.moveBy(widget.position());
+ m_translation.moveBy(widget.relativePosition());
} else {
m_translation.setX(widget.x());
m_translation.setY(widget.y());
@@ -80,8 +80,10 @@ void Painter::xorRect(const Rect& rect, Color color)
}
}
-void Painter::drawBitmap(const Point& point, const CBitmap& bitmap, Color color)
+void Painter::drawBitmap(const Point& p, const CBitmap& bitmap, Color color)
{
+ Point point = p;
+ point.moveBy(m_translation);
for (unsigned row = 0; row < bitmap.height(); ++row) {
int y = point.y() + row;
int x = point.x();
@@ -100,12 +102,10 @@ void Painter::drawText(const Rect& rect, const String& text, TextAlignment align
if (alignment == TextAlignment::TopLeft) {
point = rect.location();
- point.moveBy(m_translation);
} else if (alignment == TextAlignment::Center) {
int textWidth = text.length() * m_font.glyphWidth();
point = rect.center();
point.moveBy(-(textWidth / 2), -(m_font.glyphWidth() / 2));
- point.moveBy(m_translation);
} else {
ASSERT_NOT_REACHED();
}
diff --git a/Widgets/Rect.h b/Widgets/Rect.h
index a9e25c3760..d61ae16f7f 100644
--- a/Widgets/Rect.h
+++ b/Widgets/Rect.h
@@ -55,6 +55,18 @@ public:
int top() const { return y(); }
int bottom() const { return y() + height(); }
+ void setLeft(int left)
+ {
+ setWidth(x() - left);
+ setX(left);
+ }
+
+ void setTop(int top)
+ {
+ setHeight(y() - top);
+ setY(top);
+ }
+
int x() const { return location().x(); }
int y() const { return location().y(); }
int width() const { return m_width; }
diff --git a/Widgets/TerminalWidget.cpp b/Widgets/TerminalWidget.cpp
index 577f86b19d..570bfd2c01 100644
--- a/Widgets/TerminalWidget.cpp
+++ b/Widgets/TerminalWidget.cpp
@@ -16,7 +16,7 @@ TerminalWidget::TerminalWidget(Widget* parent)
auto& font = Font::defaultFont();
- setRect({ 0, 0, (columns() * font.glyphWidth()) + 4, (rows() * font.glyphHeight()) + 4 });
+ setWindowRelativeRect({ 0, 0, (columns() * font.glyphWidth()) + 4, (rows() * font.glyphHeight()) + 4 });
printf("rekt: %d x %d\n", width(), height());
m_screen = new CharacterWithAttributes[rows() * columns()];
diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp
index 8332abd92d..f5a66e79f1 100644
--- a/Widgets/Widget.cpp
+++ b/Widgets/Widget.cpp
@@ -16,10 +16,10 @@ Widget::~Widget()
{
}
-void Widget::setRect(const Rect& rect)
+void Widget::setWindowRelativeRect(const Rect& rect)
{
// FIXME: Make some kind of event loop driven ResizeEvent?
- m_rect = rect;
+ m_relativeRect = rect;
update();
}
@@ -102,8 +102,8 @@ Widget::HitTestResult Widget::hitTest(int x, int y)
// FIXME: Care about z-order.
for (auto* ch : children()) {
auto* child = (Widget*)ch;
- if (child->rect().contains(x, y)) {
- return child->hitTest(x - child->rect().x(), y - child->rect().y());
+ if (child->relativeRect().contains(x, y)) {
+ return child->hitTest(x - child->relativeRect().x(), y - child->relativeRect().y());
}
}
return { this, x, y };
diff --git a/Widgets/Widget.h b/Widgets/Widget.h
index dff665c4ac..2979c529e1 100644
--- a/Widgets/Widget.h
+++ b/Widgets/Widget.h
@@ -23,13 +23,15 @@ public:
virtual void onMouseDown(MouseEvent&);
virtual void onMouseUp(MouseEvent&);
- Rect rect() const { return m_rect; }
- Point position() const { return m_rect.location(); }
+ Rect relativeRect() const { return m_relativeRect; }
+ Point relativePosition() const { return m_relativeRect.location(); }
- int x() const { return rect().x(); }
- int y() const { return rect().y(); }
- int width() const { return rect().width(); }
- int height() const { return rect().height(); }
+ int x() const { return m_relativeRect.x(); }
+ int y() const { return m_relativeRect.y(); }
+ int width() const { return m_relativeRect.width(); }
+ int height() const { return m_relativeRect.height(); }
+
+ Rect rect() const { return { 0, 0, width(), height() }; }
void update();
@@ -42,7 +44,7 @@ public:
virtual const char* className() const override { return "Widget"; }
- void setRect(const Rect&);
+ void setWindowRelativeRect(const Rect&);
Color backgroundColor() const { return m_backgroundColor; }
Color foregroundColor() const { return m_foregroundColor; }
@@ -72,7 +74,7 @@ public:
private:
Window* m_window { nullptr };
- Rect m_rect;
+ Rect m_relativeRect;
Color m_backgroundColor;
Color m_foregroundColor;
diff --git a/Widgets/Window.cpp b/Widgets/Window.cpp
index 118c75cc27..2e0b95746e 100644
--- a/Widgets/Window.cpp
+++ b/Widgets/Window.cpp
@@ -46,6 +46,14 @@ void Window::event(Event& event)
if (event.isMouseEvent()) {
auto& me = static_cast<MouseEvent&>(event);
printf("Window{%p}: %s %d,%d\n", this, me.name(), me.x(), me.y());
+ if (m_mainWidget) {
+ auto result = m_mainWidget->hitTest(me.x(), me.y());
+ //printf("hit test for %d,%d found: %s{%p} %d,%d\n", me.x(), me.y(), result.widget->className(), result.widget, result.localX, result.localY);
+ // FIXME: Re-use the existing event instead of crafting a new one?
+ auto localEvent = make<MouseEvent>(event.type(), result.localX, result.localY, me.button());
+ result.widget->event(*localEvent);
+ return m_mainWidget->event(event);
+ }
return Object::event(event);
}
@@ -56,6 +64,7 @@ void Window::event(Event& event)
}
if (m_mainWidget)
return m_mainWidget->event(event);
+ return Object::event(event);
}
return Object::event(event);
diff --git a/Widgets/WindowManager.cpp b/Widgets/WindowManager.cpp
index f12405a8b9..6f1f235bb0 100644
--- a/Widgets/WindowManager.cpp
+++ b/Widgets/WindowManager.cpp
@@ -174,12 +174,6 @@ void WindowManager::processMouseEvent(MouseEvent& event)
}
}
- // Otherwise: send it to root the root widget...
- auto result = m_rootWidget->hitTest(event.x(), event.y());
- //printf("hit test for %d,%d found: %s{%p} %d,%d\n", me.x(), me.y(), result.widget->className(), result.widget, result.localX, result.localY);
- // FIXME: Re-use the existing event instead of crafting a new one?
- auto localEvent = make<MouseEvent>(event.type(), result.localX, result.localY, event.button());
- result.widget->event(*localEvent);
}
void WindowManager::handlePaintEvent(PaintEvent& event)
diff --git a/Widgets/test.cpp b/Widgets/test.cpp
index 43a5a82e58..404b80f5fb 100644
--- a/Widgets/test.cpp
+++ b/Widgets/test.cpp
@@ -7,9 +7,10 @@
#include "WindowManager.h"
#include "Window.h"
#include "ClockWidget.h"
+#include "CheckBox.h"
#include <cstdio>
-int main(int c, char** v)
+int main(int argc, char** argv)
{
FrameBufferSDL fb(800, 600);
fb.show();
@@ -21,31 +22,43 @@ int main(int c, char** v)
auto* fontTestWindow = new Window;
fontTestWindow->setTitle("Font test");
- fontTestWindow->setRect({ 100, 100, 300, 80 });
+ fontTestWindow->setRect({ 140, 100, 300, 80 });
auto* fontTestWindowWidget = new Widget;
fontTestWindow->setMainWidget(fontTestWindowWidget);
- fontTestWindowWidget->setRect({ 0, 0, 300, 80 });
+ fontTestWindowWidget->setWindowRelativeRect({ 0, 0, 300, 80 });
auto* l1 = new Label(fontTestWindowWidget);
- l1->setRect({ 0, 0, 300, 20 });
+ l1->setWindowRelativeRect({ 0, 0, 300, 20 });
l1->setText("0123456789");
auto* l2 = new Label(fontTestWindowWidget);
- l2->setRect({ 0, 20, 300, 20 });
+ l2->setWindowRelativeRect({ 0, 20, 300, 20 });
l2->setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
auto* l3 = new Label(fontTestWindowWidget);
- l3->setRect({ 0, 40, 300, 20 });
+ l3->setWindowRelativeRect({ 0, 40, 300, 20 });
l3->setText("abcdefghijklmnopqrstuvwxyz");
auto* l4 = new Label(fontTestWindowWidget);
- l4->setRect({ 0, 60, 300, 20 });
+ l4->setWindowRelativeRect({ 0, 60, 300, 20 });
l4->setText("!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~");
- auto* b = new Button(&w);
- b->setRect({ 10, 10, 100, 30 });
- b->setCaption("Button!");
+ auto* widgetTestWindow = new Window;
+ widgetTestWindow->setTitle("Widget test");
+ widgetTestWindow->setRect({ 20, 40, 100, 100 });
+
+ auto* widgetTestWindowWidget = new Widget;
+ widgetTestWindowWidget->setWindowRelativeRect({ 0, 0, 100, 100 });
+ widgetTestWindow->setMainWidget(widgetTestWindowWidget);
+
+ auto* b = new Button(widgetTestWindowWidget);
+ b->setWindowRelativeRect({ 0, 0, 100, 30 });
+ b->setCaption("Button");
+
+ auto* c = new CheckBox(widgetTestWindowWidget);
+ c->setWindowRelativeRect({ 0, 30, 100, 30 });
+ c->setCaption("CheckBox");
auto* win = new Window;
win->setTitle("Console");