LibGUI: Add a GToolBar class that can be populated with GActions.

The same action can be added to both a menu and a toolbar.
Use this to put a toolbar into FileManager. This is pretty neat. :^)
This commit is contained in:
Andreas Kling 2019-02-20 02:39:46 +01:00
parent 4804609b7e
commit b704d3d295
Notes: sideshowbarker 2024-07-19 15:40:07 +09:00
24 changed files with 196 additions and 44 deletions

View File

@ -3,47 +3,52 @@
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GStatusBar.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GAction.h>
#include <unistd.h>
#include <stdio.h>
#include "DirectoryView.h"
static GWindow* make_window();
int main(int argc, char** argv)
{
GApplication app(argc, argv);
auto mkdir_action = GAction::create("New directory...", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/mkdir16.rgb", { 16, 16 }), [] (const GAction&) {
dbgprintf("'New directory' action activated!\n");
});
auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/copyfile16.rgb", { 16, 16 }), [] (const GAction&) {
dbgprintf("'Copy' action activated!\n");
});
auto delete_action = GAction::create("Delete", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/trash16.rgb", { 16, 16 }), [] (const GAction&) {
dbgprintf("'Delete' action activated!\n");
});
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("FileManager");
app_menu->add_action(make<GAction>("Quit", String(), [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", String(), [] (const GAction&) {
GApplication::the().quit(0);
return;
}));
menubar->add_menu(move(app_menu));
auto file_menu = make<GMenu>("File");
file_menu->add_action(mkdir_action.copy_ref());
file_menu->add_action(copy_action.copy_ref());
file_menu->add_action(delete_action.copy_ref());
menubar->add_menu(move(file_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(make<GAction>("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [] (const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));
app.set_menubar(move(menubar));
auto* window = make_window();
window->set_should_exit_app_on_close(true);
window->show();
return app.exec();
}
GWindow* make_window()
{
auto* window = new GWindow;
window->set_title("FileManager");
window->set_rect(20, 200, 240, 300);
@ -53,6 +58,11 @@ GWindow* make_window()
widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
auto* toolbar = new GToolBar(widget);
toolbar->add_action(mkdir_action.copy_ref());
toolbar->add_action(copy_action.copy_ref());
toolbar->add_action(delete_action.copy_ref());
auto* directory_view = new DirectoryView(widget);
auto* statusbar = new GStatusBar(widget);
@ -68,6 +78,8 @@ GWindow* make_window()
directory_view->open("/");
return window;
}
window->set_should_exit_app_on_close(true);
window->show();
return app.exec();
}

View File

@ -97,7 +97,7 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Terminal");
app_menu->add_action(make<GAction>("Quit", String(), [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", String(), [] (const GAction&) {
dbgprintf("Terminal: Quit menu activated!\n");
GApplication::the().quit(0);
return;
@ -106,7 +106,7 @@ int main(int argc, char** argv)
auto font_menu = make<GMenu>("Font");
GFontDatabase::the().for_each_font([&] (const String& font_name) {
font_menu->add_action(make<GAction>(font_name, [&terminal] (const GAction& action) {
font_menu->add_action(GAction::create(font_name, [&terminal] (const GAction& action) {
terminal.set_font(GFontDatabase::the().get_by_name(action.text()));
terminal.force_repaint();
}));
@ -114,7 +114,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(font_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(make<GAction>("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [] (const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

BIN
Base/res/icons/mkdir16.data Normal file

Binary file not shown.

BIN
Base/res/icons/mkdir16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Base/res/icons/mkdir16.rgb Normal file

Binary file not shown.

BIN
Base/res/icons/trash16.data Normal file

Binary file not shown.

BIN
Base/res/icons/trash16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

BIN
Base/res/icons/trash16.rgb Normal file

Binary file not shown.

View File

@ -195,6 +195,36 @@ void exception_7_handler(RegisterDump& regs)
}
// 0: Divide error
EH_ENTRY_NO_CODE(0);
void exception_0_handler(RegisterDump& regs)
{
kprintf("%s DIVIDE ERROR: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters());
word ss;
dword esp;
if (current->is_ring0()) {
ss = regs.ds;
esp = regs.esp;
} else {
ss = regs.ss_if_crossRing;
esp = regs.esp_if_crossRing;
}
kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
kprintf("stk=%w:%x\n", ss, esp);
kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
if (current->is_ring0()) {
kprintf("Oh shit, we've crashed in ring 0 :(\n");
hang();
}
current->crash();
}
// 13: General Protection Fault
EH_ENTRY(13);
void exception_13_handler(RegisterDumpWithExceptionCode& regs)
@ -316,7 +346,6 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs)
hang(); \
}
EH(0, "Divide error")
EH(1, "Debug exception")
EH(2, "Unknown error")
EH(3, "Breakpoint")
@ -442,7 +471,7 @@ void idt_init()
for (byte i = 0xff; i > 0x10; --i)
register_interrupt_handler(i, unimp_trap);
register_interrupt_handler(0x00, _exception0);
register_interrupt_handler(0x00, exception_0_entry);
register_interrupt_handler(0x01, _exception1);
register_interrupt_handler(0x02, _exception2);
register_interrupt_handler(0x03, _exception3);

View File

@ -1,8 +1,8 @@
#include <LibGUI/GAction.h>
GAction::GAction(const String& text, const String& custom_data, Function<void(const GAction&)> on_activation_callback)
: m_text(text)
, on_activation(move(on_activation_callback))
: on_activation(move(on_activation_callback))
, m_text(text)
, m_custom_data(custom_data)
{
}
@ -12,6 +12,13 @@ GAction::GAction(const String& text, Function<void(const GAction&)> on_activatio
{
}
GAction::GAction(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> on_activation_callback)
: on_activation(move(on_activation_callback))
, m_text(text)
, m_icon(move(icon))
{
}
GAction::~GAction()
{
}

View File

@ -2,22 +2,41 @@
#include <AK/AKString.h>
#include <AK/Function.h>
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
#include <SharedGraphics/GraphicsBitmap.h>
class GAction {
class GAction : public Retainable<GAction> {
public:
GAction(const String& text, Function<void(const GAction&)> = nullptr);
GAction(const String& text, const String& custom_data = String(), Function<void(const GAction&)> = nullptr);
static RetainPtr<GAction> create(const String& text, Function<void(const GAction&)> callback)
{
return adopt(*new GAction(text, move(callback)));
}
static RetainPtr<GAction> create(const String& text, const String& custom_data, Function<void(const GAction&)> callback)
{
return adopt(*new GAction(text, custom_data, move(callback)));
}
static RetainPtr<GAction> create(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> callback)
{
return adopt(*new GAction(text, move(icon), move(callback)));
}
~GAction();
String text() const { return m_text; }
String custom_data() const { return m_custom_data; }
const GraphicsBitmap* icon() const { return m_icon.ptr(); }
Function<void(GAction&)> on_activation;
void activate();
private:
GAction(const String& text, Function<void(const GAction&)> = nullptr);
GAction(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> = nullptr);
GAction(const String& text, const String& custom_data = String(), Function<void(const GAction&)> = nullptr);
String m_text;
String m_custom_data;
RetainPtr<GraphicsBitmap> m_icon;
};

View File

@ -52,12 +52,14 @@ void GBoxLayout::run(GWidget& widget)
Size automatic_size;
if (m_orientation == Orientation::Horizontal) {
automatic_size.set_width(available_size.width() / number_of_entries_with_automatic_size);
automatic_size.set_height(widget.height());
} else {
automatic_size.set_width(widget.width());
automatic_size.set_height(available_size.height() / number_of_entries_with_automatic_size);
if (number_of_entries_with_automatic_size) {
if (m_orientation == Orientation::Horizontal) {
automatic_size.set_width(available_size.width() / number_of_entries_with_automatic_size);
automatic_size.set_height(widget.height());
} else {
automatic_size.set_width(widget.width());
automatic_size.set_height(available_size.height() / number_of_entries_with_automatic_size);
}
}
#ifdef GBOXLAYOUT_DEBUG

View File

@ -29,7 +29,7 @@ GMenu::~GMenu()
unrealize_menu();
}
void GMenu::add_action(OwnPtr<GAction>&& action)
void GMenu::add_action(RetainPtr<GAction>&& action)
{
m_items.append(make<GMenuItem>(move(action)));
}

View File

@ -15,7 +15,7 @@ public:
GAction* action_at(size_t);
void add_action(OwnPtr<GAction>&&);
void add_action(RetainPtr<GAction>&&);
void add_separator();
Function<void(unsigned)> on_item_activation;

View File

@ -6,7 +6,7 @@ GMenuItem::GMenuItem(Type type)
{
}
GMenuItem::GMenuItem(OwnPtr<GAction>&& action)
GMenuItem::GMenuItem(RetainPtr<GAction>&& action)
: m_type(Action)
, m_action(move(action))
{

View File

@ -9,7 +9,7 @@ public:
enum Type { Invalid, Action, Separator };
explicit GMenuItem(Type);
explicit GMenuItem(OwnPtr<GAction>&&);
explicit GMenuItem(RetainPtr<GAction>&&);
~GMenuItem();
Type type() const { return m_type; }
@ -21,6 +21,6 @@ public:
private:
Type m_type { Invalid };
unsigned m_identifier { 0 };
OwnPtr<GAction> m_action;
RetainPtr<GAction> m_action;
};

58
LibGUI/GToolBar.cpp Normal file
View File

@ -0,0 +1,58 @@
#include <LibGUI/GToolBar.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GAction.h>
#include <SharedGraphics/Painter.h>
GToolBar::GToolBar(GWidget* parent)
: GWidget(parent)
{
set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
set_preferred_size({ 0, 24 });
set_layout(make<GBoxLayout>(Orientation::Horizontal));
}
GToolBar::~GToolBar()
{
}
void GToolBar::add_action(RetainPtr<GAction>&& action)
{
ASSERT(action);
GAction* raw_action_ptr = action.ptr();
auto item = make<Item>();
item->type = Item::Action;
item->action = move(action);
auto* button = new GButton(this);
if (item->action->icon())
button->set_icon(item->action->icon());
else
button->set_caption(item->action->text());
button->on_click = [raw_action_ptr] (const GButton&) {
raw_action_ptr->activate();
};
#if 0
// FIXME: Gotta fix GBoxLayout for this to work.
button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
button->set_preferred_size({ 16, 16 });
#endif
m_items.append(move(item));
}
void GToolBar::add_separator()
{
auto item = make<Item>();
item->type = Item::Separator;
m_items.append(move(item));
}
void GToolBar::paint_event(GPaintEvent& event)
{
Painter painter(*this);
painter.set_clip_rect(event.rect());
painter.fill_rect({ 0, 0, width(), height() - 1 }, Color::LightGray);
painter.draw_line({ 0, rect().bottom() }, { width() - 1, rect().bottom() }, Color::DarkGray);
}

25
LibGUI/GToolBar.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <LibGUI/GWidget.h>
class GAction;
class GToolBar : public GWidget {
public:
explicit GToolBar(GWidget* parent);
virtual ~GToolBar() override;
void add_action(RetainPtr<GAction>&&);
void add_separator();
private:
virtual const char* class_name() const override { return "GToolBar"; }
virtual void paint_event(GPaintEvent&) override;
struct Item {
enum Type { Invalid, Separator, Action };
Type type { Invalid };
RetainPtr<GAction> action;
};
Vector<OwnPtr<Item>> m_items;
};

View File

@ -73,6 +73,13 @@ void GWidget::handle_paint_event(GPaintEvent& event)
if (fill_with_background_color()) {
Painter painter(*this);
painter.fill_rect(event.rect(), background_color());
} else {
#ifdef DEBUG_WIDGET_UNDERDRAW
// FIXME: This is a bit broken.
// If the widget is not opaque, let's not mess it up with debugging color.
Painter painter(*this);
painter.fill_rect(rect(), Color::Red);
#endif
}
paint_event(event);
for (auto* ch : children()) {

View File

@ -27,6 +27,7 @@ LIBGUI_OBJS = \
GApplication.o \
GAction.o \
GFontDatabase.o \
GToolBar.o \
GWindow.o
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)

View File

@ -14,8 +14,6 @@
#include <LibC/string.h>
#endif
#define DEBUG_WIDGET_UNDERDRAW
Painter::Painter(GraphicsBitmap& bitmap)
{
m_font = &Font::default_font();
@ -42,12 +40,6 @@ Painter::Painter(GWidget& widget)
// NOTE: m_clip_rect is in Window coordinates since we are painting into its backing store.
m_clip_rect = widget.window_relative_rect();
m_clip_rect.intersect(m_target->rect());
#ifdef DEBUG_WIDGET_UNDERDRAW
// If the widget is not opaque, let's not mess it up with debugging color.
if (widget.fill_with_background_color() && m_window->main_widget() != &widget)
fill_rect(widget.rect(), Color::Red);
#endif
}
#endif