Move Widget & friends into LibGUI.

This commit is contained in:
Andreas Kling 2019-01-19 23:49:56 +01:00
parent 7e5b81fe48
commit a026da47e7
Notes: sideshowbarker 2024-07-19 16:00:07 +09:00
29 changed files with 73 additions and 551 deletions

View file

@ -4,6 +4,8 @@ sudo id
make -C ../LibC clean && \
make -C ../LibC && \
make -C ../LibGUI clean && \
make -C ../LibGUI && \
make -C ../Userland clean && \
make -C ../Userland && \
make -C ../Terminal clean && \

View file

@ -1,5 +1,5 @@
#include "Button.h"
#include "Painter.h"
#include <SharedGraphics/Painter.h>
Button::Button(Widget* parent)
: Widget(parent)
@ -66,7 +66,7 @@ void Button::paintEvent(PaintEvent&)
void Button::mouseDownEvent(MouseEvent& event)
{
printf("Button::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
dbgprintf("Button::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
m_beingPressed = true;
@ -76,7 +76,7 @@ void Button::mouseDownEvent(MouseEvent& event)
void Button::mouseUpEvent(MouseEvent& event)
{
printf("Button::mouseUpEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
dbgprintf("Button::mouseUpEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
m_beingPressed = false;

View file

@ -1,6 +1,6 @@
#include "CheckBox.h"
#include "Painter.h"
#include "CharacterBitmap.h"
#include <SharedGraphics/Painter.h>
#include <SharedGraphics/CharacterBitmap.h>
CheckBox::CheckBox(Widget* parent)
: Widget(parent)
@ -95,7 +95,7 @@ void CheckBox::paintEvent(PaintEvent&)
void CheckBox::mouseDownEvent(MouseEvent& event)
{
printf("CheckBox::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
dbgprintf("CheckBox::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button());
setIsChecked(!isChecked());
}

View file

@ -1,7 +1,7 @@
#pragma once
#include "Point.h"
#include "Rect.h"
#include <SharedGraphics/Point.h>
#include <SharedGraphics/Rect.h>
#include <AK/AKString.h>
#include <AK/Types.h>

View file

@ -1,10 +1,6 @@
#include "EventLoop.h"
#include "Event.h"
#include "Object.h"
#include "WindowManager.h"
#include "AbstractScreen.h"
#include "PS2MouseDevice.h"
#include "Scheduler.h"
static EventLoop* s_mainEventLoop;
@ -31,14 +27,12 @@ EventLoop& EventLoop::main()
int EventLoop::exec()
{
m_server_process = current;
m_running = true;
for (;;) {
if (m_queuedEvents.is_empty())
waitForEvent();
Vector<QueuedEvent> events;
{
InterruptDisabler disabler;
events = move(m_queuedEvents);
}
for (auto& queuedEvent : events) {
@ -51,7 +45,7 @@ int EventLoop::exec()
ASSERT_NOT_REACHED();
return 0;
default:
printf("event type %u with no receiver :(\n", event.type());
dbgprintf("event type %u with no receiver :(\n", event.type());
ASSERT_NOT_REACHED();
return 1;
}
@ -70,26 +64,4 @@ void EventLoop::postEvent(Object* receiver, OwnPtr<Event>&& event)
void EventLoop::waitForEvent()
{
auto& mouse = PS2MouseDevice::the();
auto& screen = AbstractScreen::the();
bool prev_left_button = screen.left_mouse_button_pressed();
bool prev_right_button = screen.right_mouse_button_pressed();
int dx = 0;
int dy = 0;
while (mouse.can_read(*m_server_process)) {
signed_byte data[3];
ssize_t nread = mouse.read(*m_server_process, (byte*)data, 3);
ASSERT(nread == 3);
bool left_button = data[0] & 1;
bool right_button = data[0] & 2;
dx += data[1];
dy += -data[2];
if (left_button != prev_left_button || right_button != prev_right_button || !mouse.can_read(*m_server_process)) {
prev_left_button = left_button;
prev_right_button = right_button;
screen.on_receive_mouse_data(dx, dy, left_button, right_button);
dx = 0;
dy = 0;
}
}
}

View file

@ -1,5 +1,5 @@
#include "Label.h"
#include "Painter.h"
#include <SharedGraphics/Painter.h>
Label::Label(Widget* parent)
: Widget(parent)
@ -29,7 +29,7 @@ void Label::paintEvent(PaintEvent&)
void Label::mouseMoveEvent(MouseEvent& event)
{
printf("Label::mouseMoveEvent: x=%d, y=%d\n", event.x(), event.y());
dbgprintf("Label::mouseMoveEvent: x=%d, y=%d\n", event.x(), event.y());
Widget::mouseMoveEvent(event);
}

View file

@ -1,7 +1,7 @@
#include "ListBox.h"
#include "Painter.h"
#include "Font.h"
#include "Window.h"
#include <SharedGraphics/Font.h>
#include <SharedGraphics/Painter.h>
ListBox::ListBox(Widget* parent)
: Widget(parent)
@ -47,12 +47,12 @@ void ListBox::paintEvent(PaintEvent&)
void ListBox::mouseDownEvent(MouseEvent& event)
{
printf("ListBox::mouseDownEvent %d,%d\n", event.x(), event.y());
dbgprintf("ListBox::mouseDownEvent %d,%d\n", event.x(), event.y());
for (int i = m_scrollOffset; i < static_cast<int>(m_items.size()); ++i) {
auto itemRect = item_rect(i);
if (itemRect.contains(event.position())) {
m_selectedIndex = i;
printf("ListBox: selected item %u (\"%s\")\n", i, m_items[i].characters());
dbgprintf("ListBox: selected item %u (\"%s\")\n", i, m_items[i].characters());
update();
return;
}

View file

@ -57,7 +57,7 @@ void Object::timerEvent(TimerEvent&)
void Object::startTimer(int ms)
{
if (m_timerID) {
printf("Object{%p} already has a timer!\n", this);
dbgprintf("Object{%p} already has a timer!\n", this);
ASSERT_NOT_REACHED();
}
}

View file

@ -1,8 +1,8 @@
#include "TextBox.h"
#include "Painter.h"
#include "Font.h"
#include "CharacterBitmap.h"
#include <AK/StdLibExtras.h>
#include <SharedGraphics/CharacterBitmap.h>
#include <SharedGraphics/Font.h>
#include <SharedGraphics/Painter.h>
TextBox::TextBox(Widget* parent)
: Widget(parent)
@ -48,7 +48,7 @@ void TextBox::paintEvent(PaintEvent&)
int x = innerRect.x() + (i * font().glyph_width());
auto* bitmap = font().glyph_bitmap(ch);
if (!bitmap) {
printf("TextBox: glyph missing: %02x\n", ch);
dbgprintf("TextBox: glyph missing: %02x\n", ch);
ASSERT_NOT_REACHED();
}
painter.draw_bitmap({x, y}, *bitmap, Color::Black);

View file

@ -1,11 +1,10 @@
#include "Widget.h"
#include "Event.h"
#include "EventLoop.h"
#include "GraphicsBitmap.h"
#include "WindowManager.h"
#include "Window.h"
#include "Painter.h"
#include <AK/Assertions.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <SharedGraphics/Painter.h>
Widget::Widget(Widget* parent)
: Object(parent)

View file

@ -2,9 +2,9 @@
#include "Event.h"
#include "Object.h"
#include "Rect.h"
#include "Color.h"
#include "Font.h"
#include <SharedGraphics/Rect.h>
#include <SharedGraphics/Color.h>
#include <SharedGraphics/Font.h>
#include <AK/AKString.h>
class GraphicsBitmap;

42
LibGUI/Window.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "Window.h"
#include "Event.h"
#include "EventLoop.h"
#include <SharedGraphics/GraphicsBitmap.h>
Window::Window(int window_id)
: m_window_id(window_id)
{
}
Window::~Window()
{
}
void Window::set_title(String&& title)
{
if (m_title == title)
return;
m_title = move(title);
}
void Window::set_rect(const Rect& rect)
{
if (m_rect == rect)
return;
m_rect = rect;
dbgprintf("Window::setRect %d,%d %dx%d\n", m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height());
}
void Window::event(Event& event)
{
}
bool Window::is_visible() const
{
return false;
}
void Window::close()
{
}

View file

@ -1,16 +1,13 @@
#pragma once
#include "Object.h"
#include "Rect.h"
#include "GraphicsBitmap.h"
#include <SharedGraphics/Rect.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <AK/AKString.h>
#include <AK/InlineLinkedList.h>
class Process;
class Window final : public Object, public InlineLinkedListNode<Window> {
class Window final : public Object {
public:
Window(Process&, int window_id);
explicit Window(int window_id);
virtual ~Window() override;
int window_id() const { return m_window_id; }
@ -41,18 +38,12 @@ public:
GraphicsBitmap* backing() { return m_backing.ptr(); }
// For InlineLinkedList.
// FIXME: Maybe make a ListHashSet and then WindowManager can just use that.
Window* m_next { nullptr };
Window* m_prev { nullptr };
private:
String m_title;
Rect m_rect;
bool m_is_being_dragged { false };
RetainPtr<GraphicsBitmap> m_backing;
Process& m_process;
int m_window_id { -1 };
};

View file

@ -1,61 +0,0 @@
#include "MsgBox.h"
#include "Font.h"
#include "AbstractScreen.h"
#include "Window.h"
#include "Label.h"
#include "Button.h"
#include "Process.h"
#if 0
void MsgBox(Window* owner, String&& text)
{
Font& font = Font::defaultFont();
auto screenRect = AbstractScreen::the().rect();
int textWidth = text.length() * font.glyphWidth() + 8;
int textHeight = font.glyphHeight() + 8;
int horizontalPadding = 16;
int verticalPadding = 16;
int buttonWidth = 60;
int buttonHeight = 20;
int windowWidth = textWidth + horizontalPadding * 2;
int windowHeight = textHeight + buttonHeight + verticalPadding * 3;
Rect windowRect(
screenRect.center().x() - windowWidth / 2,
screenRect.center().y() - windowHeight / 2,
windowWidth,
windowHeight
);
Rect buttonRect(
windowWidth / 2 - buttonWidth / 2,
windowHeight - verticalPadding - buttonHeight,
buttonWidth,
buttonHeight
);
auto* window = new Window(*current, current->make_window_id());
window->setTitle("MsgBox");
window->setRect(windowRect);
auto* widget = new Widget;
widget->setWindowRelativeRect({ 0, 0, windowWidth, windowHeight });
widget->setFillWithBackgroundColor(true);
window->setMainWidget(widget);
auto* label = new Label(widget);
label->setWindowRelativeRect({
horizontalPadding,
verticalPadding,
textWidth,
textHeight
});
label->setText(move(text));
auto* button = new Button(widget);
button->setCaption("OK");
button->setWindowRelativeRect(buttonRect);
button->onClick = [] (Button& button) {
printf("MsgBox button pressed, closing MsgBox :)\n");
button.window()->close();
};
}
#endif

View file

@ -1,8 +0,0 @@
#pragma once
#include <AK/AKString.h>
class Window;
void MsgBox(Window* owner, String&&);

View file

@ -1,175 +0,0 @@
#include "TerminalWidget.h"
#include "Font.h"
#include "Painter.h"
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
extern int g_fd;
TerminalWidget* g_tw;
TerminalWidget::TerminalWidget(Widget* parent)
: Widget(parent)
{
g_tw = this;
setWindowRelativeRect({ 0, 0, int(columns() * font().glyph_width()) + 4, int(rows() * font().glyph_height()) + 4 });
printf("rekt: %d x %d\n", width(), height());
m_screen = new CharacterWithAttributes[rows() * columns()];
for (unsigned row = 0; row < m_rows; ++row) {
for (unsigned column = 0; column < m_columns; ++column) {
at(row, column).character = ' ';
at(row, column).attribute = 0x07;
}
}
#if __APPLE__
g_fd = posix_openpt(O_RDWR);
#else
g_fd = getpt();
#endif
grantpt(g_fd);
unlockpt(g_fd);
char buf[1024];
ptsname_r(g_fd, buf, sizeof(buf));
if (fork() == 0) {
close(g_fd);
setsid();
int fd = open(buf, O_RDWR);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
signal(SIGWINCH, SIG_IGN);
ioctl(fd, TIOCSCTTY);
execl("/bin/bash", "bash", nullptr);
ASSERT_NOT_REACHED();
}
signal(SIGCHLD, SIG_IGN);
}
TerminalWidget::~TerminalWidget()
{
}
CharacterWithAttributes& TerminalWidget::at(unsigned row, unsigned column)
{
ASSERT(m_screen);
ASSERT(row < m_rows);
ASSERT(column < m_columns);
return m_screen[row * columns() + column];
}
void TerminalWidget::paintEvent(PaintEvent&)
{
Painter painter(*this);
painter.fill_rect(rect(), Color::Black);
char buf[2] = { 0, 0 };
for (unsigned row = 0; row < m_rows; ++row) {
int y = row * font().glyph_height();
for (unsigned column = 0; column < m_columns; ++column) {
int x = column * font().glyph_width();
buf[0] = at(row, column).character;
painter.draw_text({ x + 2, y + 2, width(), font().glyph_height() }, buf, Painter::TextAlignment::TopLeft, Color(0xa0, 0xa0, 0xa0));
}
}
if (m_belling)
painter.draw_rect(rect(), Color::Red);
}
void TerminalWidget::onReceive(const ByteBuffer& buffer)
{
for (unsigned i = 0; i < buffer.size(); ++i) {
onReceive(buffer[i]);
}
}
void TerminalWidget::onReceive(byte ch)
{
//printf("receive %02x\n", ch);
auto scrollScreen = [&] () {
memmove(m_screen, m_screen + columns(), (m_rows - 1) * columns() * sizeof(CharacterWithAttributes));
memset(m_screen + (m_rows - 1) * columns(), ' ', columns() * sizeof(CharacterWithAttributes));
};
auto addChar = [&] (byte ch) {
at(m_cursorRow, m_cursorColumn).character = ch;
if (++m_cursorColumn >= m_columns) {
m_cursorColumn = 0;
if (m_cursorRow < (m_rows - 1)) {
++m_cursorRow;
} else {
scrollScreen();
}
}
};
switch (ch) {
case '\n':
if (m_cursorRow < (m_rows - 1)) {
++m_cursorRow;
} else {
scrollScreen();
}
break;
case '\r':
m_cursorColumn = 0;
break;
case '\t':
// FIXME: Respect terminal tab stops.
while ((m_cursorColumn % 8) != 0 && m_cursorColumn < m_columns) {
addChar(' ');
break;
case '\a':
bell();
break;
case 8:
if (m_cursorColumn > 0) {
--m_cursorColumn;
at(m_cursorRow, m_cursorColumn).character = ' ';
}
break;
case 27:
printf("TerminalWidget: got escape!\n");
break;
default:
addChar(ch);
break;
}
}
update();
}
void TerminalWidget::keyDownEvent(KeyEvent& event)
{
if (event.text().is_empty())
return;
write(g_fd, event.text().characters(), event.text().length());
}
void TerminalWidget::keyUpEvent(KeyEvent& event)
{
return Widget::keyUpEvent(event);
}
void TerminalWidget::bell()
{
if (m_belling)
stopTimer();
startTimer(250);
m_belling = true;
update();
}
void TerminalWidget::timerEvent(TimerEvent&)
{
m_belling = false;
stopTimer();
update();
}

View file

@ -1,41 +0,0 @@
#pragma once
#include "Widget.h"
#include <AK/ByteBuffer.h>
struct CharacterWithAttributes {
byte character;
byte attribute;
};
class TerminalWidget final : public Widget {
public:
explicit TerminalWidget(Widget* parent);
virtual ~TerminalWidget() override;
unsigned rows() const { return m_rows; }
unsigned columns() const { return m_columns; }
void onReceive(const ByteBuffer&);
void onReceive(byte);
private:
CharacterWithAttributes& at(unsigned row, unsigned column);
virtual void paintEvent(PaintEvent&) override;
virtual void keyDownEvent(KeyEvent&) override;
virtual void keyUpEvent(KeyEvent&) override;
virtual void timerEvent(TimerEvent&) override;
void bell();
unsigned m_columns { 80 };
unsigned m_rows { 25 };
unsigned m_cursorRow { 0 };
unsigned m_cursorColumn { 0 };
CharacterWithAttributes* m_screen { nullptr };
bool m_belling { false };
};

View file

@ -1,99 +0,0 @@
#include "Window.h"
#include "WindowManager.h"
#include "Event.h"
#include "EventLoop.h"
#include "Process.h"
Window::Window(Process& process, int window_id)
: m_process(process)
, m_window_id(window_id)
{
WindowManager::the().addWindow(*this);
}
Window::~Window()
{
WindowManager::the().removeWindow(*this);
}
void Window::set_title(String&& title)
{
if (m_title == title)
return;
m_title = move(title);
WindowManager::the().notifyTitleChanged(*this);
}
void Window::set_rect(const Rect& rect)
{
if (m_rect == rect)
return;
auto oldRect = m_rect;
m_rect = rect;
dbgprintf("Window::setRect %d,%d %dx%d\n", m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height());
m_backing = GraphicsBitmap::create(m_process, m_rect.size());
WindowManager::the().notifyRectChanged(*this, oldRect, m_rect);
}
// FIXME: Just use the same types.
static GUI_MouseButton to_api(MouseButton button)
{
switch (button) {
case MouseButton::None: return GUI_MouseButton::NoButton;
case MouseButton::Left: return GUI_MouseButton::Left;
case MouseButton::Right: return GUI_MouseButton::Right;
case MouseButton::Middle: return GUI_MouseButton::Middle;
}
}
void Window::event(Event& event)
{
GUI_Event gui_event;
gui_event.window_id = window_id();
switch (event.type()) {
case Event::Paint:
gui_event.type = GUI_Event::Type::Paint;
gui_event.paint.rect = static_cast<PaintEvent&>(event).rect();
break;
case Event::MouseMove:
gui_event.type = GUI_Event::Type::MouseMove;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
break;
case Event::MouseDown:
gui_event.type = GUI_Event::Type::MouseDown;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
gui_event.mouse.button = to_api(static_cast<MouseEvent&>(event).button());
break;
case Event::MouseUp:
gui_event.type = GUI_Event::Type::MouseUp;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
gui_event.mouse.button = to_api(static_cast<MouseEvent&>(event).button());
break;
case Event::KeyDown:
gui_event.type = GUI_Event::Type::KeyDown;
gui_event.key.character = static_cast<KeyEvent&>(event).text()[0];
break;
}
if (gui_event.type == GUI_Event::Type::Invalid)
return;
{
LOCKER(m_process.gui_events_lock());
m_process.gui_events().append(move(gui_event));
}
}
bool Window::is_visible() const
{
return WindowManager::the().isVisible(const_cast<Window&>(*this));
}
void Window::close()
{
WindowManager::the().removeWindow(*this);
deleteLater();
}

View file

@ -1,100 +0,0 @@
#include "FrameBuffer.h"
#include "EventLoop.h"
#include "Label.h"
#include "Button.h"
#include "WindowManager.h"
#include "Window.h"
#include "ClockWidget.h"
#include "CheckBox.h"
#include "ListBox.h"
#include "TextBox.h"
#include "MsgBox.h"
#include <cstdio>
int main(int argc, char** argv)
{
FrameBuffer fb(800, 600);
fb.show();
EventLoop loop;
auto* fontTestWindow = new Window;
fontTestWindow->set_title("Font test");
fontTestWindow->set_rect({ 140, 100, 300, 80 });
auto* fontTestWindowWidget = new Widget;
fontTestWindow->setMainWidget(fontTestWindowWidget);
fontTestWindowWidget->setWindowRelativeRect({ 0, 0, 300, 80 });
auto* l1 = new Label(fontTestWindowWidget);
l1->setWindowRelativeRect({ 0, 0, 300, 20 });
l1->setText("0123456789");
auto* l2 = new Label(fontTestWindowWidget);
l2->setWindowRelativeRect({ 0, 20, 300, 20 });
l2->setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
auto* l3 = new Label(fontTestWindowWidget);
l3->setWindowRelativeRect({ 0, 40, 300, 20 });
l3->setText("abcdefghijklmnopqrstuvwxyz");
auto* l4 = new Label(fontTestWindowWidget);
l4->setWindowRelativeRect({ 0, 60, 300, 20 });
l4->setText("!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~");
{
auto* widgetTestWindow = new Window;
widgetTestWindow->set_title("Widget test");
widgetTestWindow->set_rect({ 20, 40, 100, 180 });
auto* widgetTestWindowWidget = new Widget;
widgetTestWindowWidget->setWindowRelativeRect({ 0, 0, 100, 100 });
widgetTestWindow->setMainWidget(widgetTestWindowWidget);
auto* l = new Label(widgetTestWindowWidget);
l->setWindowRelativeRect({ 0, 0, 100, 20 });
l->setText("Label");
auto* b = new Button(widgetTestWindowWidget);
b->setWindowRelativeRect({ 0, 20, 100, 20 });
b->setCaption("Button");
b->onClick = [] (Button& button) {
printf("Button %p clicked!\n", &button);
};
auto* c = new CheckBox(widgetTestWindowWidget);
c->setWindowRelativeRect({ 0, 40, 100, 20 });
c->setCaption("CheckBox");
auto *lb = new ListBox(widgetTestWindowWidget);
lb->setWindowRelativeRect({ 0, 60, 100, 100 });
lb->addItem("This");
lb->addItem("is");
lb->addItem("a");
lb->addItem("ListBox");
auto *tb = new TextBox(widgetTestWindowWidget);
tb->setWindowRelativeRect({ 0, 160, 100, 20 });
tb->setText("Hello!");
tb->setFocus(true);
tb->onReturnPressed = [] (TextBox& textBox) {
printf("TextBox %p return pressed: '%s'\n", &textBox, textBox.text().characters());
MsgBox(nullptr, textBox.text());
};
WindowManager::the().setActiveWindow(widgetTestWindow);
}
#if 0
auto* clockWin = new Window;
clockWin->setTitle("Clock");
clockWin->setRect({ 500, 50, 100, 40 });
clockWin->setMainWidget(new ClockWidget);
#endif
MsgBox(nullptr, "This is a message box!");
return loop.exec();
}