mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:57:45 +00:00
Add a CheckBox widget.
This commit is contained in:
parent
c7463aad11
commit
16576112b0
13 changed files with 177 additions and 36 deletions
|
@ -22,9 +22,9 @@ void Button::setCaption(String&& caption)
|
||||||
void Button::onPaint(PaintEvent&)
|
void Button::onPaint(PaintEvent&)
|
||||||
{
|
{
|
||||||
Painter painter(*this);
|
Painter painter(*this);
|
||||||
painter.fillRect({ 0, 0, width(), height() }, backgroundColor());
|
painter.fillRect(rect(), backgroundColor());
|
||||||
if (!caption().isEmpty()) {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
Widgets/CheckBox.cpp
Normal file
84
Widgets/CheckBox.cpp
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
|
26
Widgets/CheckBox.h
Normal file
26
Widgets/CheckBox.h
Normal file
|
@ -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 };
|
||||||
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
ClockWidget::ClockWidget(Widget* parent)
|
ClockWidget::ClockWidget(Widget* parent)
|
||||||
: Widget(parent)
|
: Widget(parent)
|
||||||
{
|
{
|
||||||
setRect({ 0, 0, 100, 40 });
|
setWindowRelativeRect({ 0, 0, 100, 40 });
|
||||||
startTimer(250);
|
startTimer(250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ VFS_OBJS = \
|
||||||
Window.o \
|
Window.o \
|
||||||
ClockWidget.o \
|
ClockWidget.o \
|
||||||
CBitmap.o \
|
CBitmap.o \
|
||||||
|
CheckBox.o \
|
||||||
test.o
|
test.o
|
||||||
|
|
||||||
OBJS = $(AK_OBJS) $(VFS_OBJS)
|
OBJS = $(AK_OBJS) $(VFS_OBJS)
|
||||||
|
|
|
@ -12,7 +12,7 @@ Painter::Painter(Widget& widget)
|
||||||
{
|
{
|
||||||
if (auto* window = widget.window()) {
|
if (auto* window = widget.window()) {
|
||||||
m_translation = window->position();
|
m_translation = window->position();
|
||||||
m_translation.moveBy(widget.position());
|
m_translation.moveBy(widget.relativePosition());
|
||||||
} else {
|
} else {
|
||||||
m_translation.setX(widget.x());
|
m_translation.setX(widget.x());
|
||||||
m_translation.setY(widget.y());
|
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) {
|
for (unsigned row = 0; row < bitmap.height(); ++row) {
|
||||||
int y = point.y() + row;
|
int y = point.y() + row;
|
||||||
int x = point.x();
|
int x = point.x();
|
||||||
|
@ -100,12 +102,10 @@ void Painter::drawText(const Rect& rect, const String& text, TextAlignment align
|
||||||
|
|
||||||
if (alignment == TextAlignment::TopLeft) {
|
if (alignment == TextAlignment::TopLeft) {
|
||||||
point = rect.location();
|
point = rect.location();
|
||||||
point.moveBy(m_translation);
|
|
||||||
} else if (alignment == TextAlignment::Center) {
|
} else if (alignment == TextAlignment::Center) {
|
||||||
int textWidth = text.length() * m_font.glyphWidth();
|
int textWidth = text.length() * m_font.glyphWidth();
|
||||||
point = rect.center();
|
point = rect.center();
|
||||||
point.moveBy(-(textWidth / 2), -(m_font.glyphWidth() / 2));
|
point.moveBy(-(textWidth / 2), -(m_font.glyphWidth() / 2));
|
||||||
point.moveBy(m_translation);
|
|
||||||
} else {
|
} else {
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,18 @@ public:
|
||||||
int top() const { return y(); }
|
int top() const { return y(); }
|
||||||
int bottom() const { return y() + height(); }
|
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 x() const { return location().x(); }
|
||||||
int y() const { return location().y(); }
|
int y() const { return location().y(); }
|
||||||
int width() const { return m_width; }
|
int width() const { return m_width; }
|
||||||
|
|
|
@ -16,7 +16,7 @@ TerminalWidget::TerminalWidget(Widget* parent)
|
||||||
|
|
||||||
auto& font = Font::defaultFont();
|
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());
|
printf("rekt: %d x %d\n", width(), height());
|
||||||
m_screen = new CharacterWithAttributes[rows() * columns()];
|
m_screen = new CharacterWithAttributes[rows() * columns()];
|
||||||
|
|
|
@ -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?
|
// FIXME: Make some kind of event loop driven ResizeEvent?
|
||||||
m_rect = rect;
|
m_relativeRect = rect;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +102,8 @@ Widget::HitTestResult Widget::hitTest(int x, int y)
|
||||||
// FIXME: Care about z-order.
|
// FIXME: Care about z-order.
|
||||||
for (auto* ch : children()) {
|
for (auto* ch : children()) {
|
||||||
auto* child = (Widget*)ch;
|
auto* child = (Widget*)ch;
|
||||||
if (child->rect().contains(x, y)) {
|
if (child->relativeRect().contains(x, y)) {
|
||||||
return child->hitTest(x - child->rect().x(), y - child->rect().y());
|
return child->hitTest(x - child->relativeRect().x(), y - child->relativeRect().y());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { this, x, y };
|
return { this, x, y };
|
||||||
|
|
|
@ -23,13 +23,15 @@ public:
|
||||||
virtual void onMouseDown(MouseEvent&);
|
virtual void onMouseDown(MouseEvent&);
|
||||||
virtual void onMouseUp(MouseEvent&);
|
virtual void onMouseUp(MouseEvent&);
|
||||||
|
|
||||||
Rect rect() const { return m_rect; }
|
Rect relativeRect() const { return m_relativeRect; }
|
||||||
Point position() const { return m_rect.location(); }
|
Point relativePosition() const { return m_relativeRect.location(); }
|
||||||
|
|
||||||
int x() const { return rect().x(); }
|
int x() const { return m_relativeRect.x(); }
|
||||||
int y() const { return rect().y(); }
|
int y() const { return m_relativeRect.y(); }
|
||||||
int width() const { return rect().width(); }
|
int width() const { return m_relativeRect.width(); }
|
||||||
int height() const { return rect().height(); }
|
int height() const { return m_relativeRect.height(); }
|
||||||
|
|
||||||
|
Rect rect() const { return { 0, 0, width(), height() }; }
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ public:
|
||||||
|
|
||||||
virtual const char* className() const override { return "Widget"; }
|
virtual const char* className() const override { return "Widget"; }
|
||||||
|
|
||||||
void setRect(const Rect&);
|
void setWindowRelativeRect(const Rect&);
|
||||||
|
|
||||||
Color backgroundColor() const { return m_backgroundColor; }
|
Color backgroundColor() const { return m_backgroundColor; }
|
||||||
Color foregroundColor() const { return m_foregroundColor; }
|
Color foregroundColor() const { return m_foregroundColor; }
|
||||||
|
@ -72,7 +74,7 @@ public:
|
||||||
private:
|
private:
|
||||||
Window* m_window { nullptr };
|
Window* m_window { nullptr };
|
||||||
|
|
||||||
Rect m_rect;
|
Rect m_relativeRect;
|
||||||
Color m_backgroundColor;
|
Color m_backgroundColor;
|
||||||
Color m_foregroundColor;
|
Color m_foregroundColor;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,14 @@ void Window::event(Event& event)
|
||||||
if (event.isMouseEvent()) {
|
if (event.isMouseEvent()) {
|
||||||
auto& me = static_cast<MouseEvent&>(event);
|
auto& me = static_cast<MouseEvent&>(event);
|
||||||
printf("Window{%p}: %s %d,%d\n", this, me.name(), me.x(), me.y());
|
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);
|
return Object::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +64,7 @@ void Window::event(Event& event)
|
||||||
}
|
}
|
||||||
if (m_mainWidget)
|
if (m_mainWidget)
|
||||||
return m_mainWidget->event(event);
|
return m_mainWidget->event(event);
|
||||||
|
return Object::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object::event(event);
|
return Object::event(event);
|
||||||
|
|
|
@ -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)
|
void WindowManager::handlePaintEvent(PaintEvent& event)
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
#include "WindowManager.h"
|
#include "WindowManager.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "ClockWidget.h"
|
#include "ClockWidget.h"
|
||||||
|
#include "CheckBox.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
int main(int c, char** v)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
FrameBufferSDL fb(800, 600);
|
FrameBufferSDL fb(800, 600);
|
||||||
fb.show();
|
fb.show();
|
||||||
|
@ -21,31 +22,43 @@ int main(int c, char** v)
|
||||||
|
|
||||||
auto* fontTestWindow = new Window;
|
auto* fontTestWindow = new Window;
|
||||||
fontTestWindow->setTitle("Font test");
|
fontTestWindow->setTitle("Font test");
|
||||||
fontTestWindow->setRect({ 100, 100, 300, 80 });
|
fontTestWindow->setRect({ 140, 100, 300, 80 });
|
||||||
|
|
||||||
auto* fontTestWindowWidget = new Widget;
|
auto* fontTestWindowWidget = new Widget;
|
||||||
fontTestWindow->setMainWidget(fontTestWindowWidget);
|
fontTestWindow->setMainWidget(fontTestWindowWidget);
|
||||||
fontTestWindowWidget->setRect({ 0, 0, 300, 80 });
|
fontTestWindowWidget->setWindowRelativeRect({ 0, 0, 300, 80 });
|
||||||
|
|
||||||
auto* l1 = new Label(fontTestWindowWidget);
|
auto* l1 = new Label(fontTestWindowWidget);
|
||||||
l1->setRect({ 0, 0, 300, 20 });
|
l1->setWindowRelativeRect({ 0, 0, 300, 20 });
|
||||||
l1->setText("0123456789");
|
l1->setText("0123456789");
|
||||||
|
|
||||||
auto* l2 = new Label(fontTestWindowWidget);
|
auto* l2 = new Label(fontTestWindowWidget);
|
||||||
l2->setRect({ 0, 20, 300, 20 });
|
l2->setWindowRelativeRect({ 0, 20, 300, 20 });
|
||||||
l2->setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
l2->setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||||
|
|
||||||
auto* l3 = new Label(fontTestWindowWidget);
|
auto* l3 = new Label(fontTestWindowWidget);
|
||||||
l3->setRect({ 0, 40, 300, 20 });
|
l3->setWindowRelativeRect({ 0, 40, 300, 20 });
|
||||||
l3->setText("abcdefghijklmnopqrstuvwxyz");
|
l3->setText("abcdefghijklmnopqrstuvwxyz");
|
||||||
|
|
||||||
auto* l4 = new Label(fontTestWindowWidget);
|
auto* l4 = new Label(fontTestWindowWidget);
|
||||||
l4->setRect({ 0, 60, 300, 20 });
|
l4->setWindowRelativeRect({ 0, 60, 300, 20 });
|
||||||
l4->setText("!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~");
|
l4->setText("!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~");
|
||||||
|
|
||||||
auto* b = new Button(&w);
|
auto* widgetTestWindow = new Window;
|
||||||
b->setRect({ 10, 10, 100, 30 });
|
widgetTestWindow->setTitle("Widget test");
|
||||||
b->setCaption("Button!");
|
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;
|
auto* win = new Window;
|
||||||
win->setTitle("Console");
|
win->setTitle("Console");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue