Skip to content

Commit

Permalink
LibGUI: Start adding an automatic widget layout system.
Browse files Browse the repository at this point in the history
My needs are really quite simple, so I'm just going to add what I need
as I go along. The first thing I needed was a simple box layout with
widgets being able to say whether they prefer fixed or fill for both
their vertical and horizontal sizes.

I also made a simple GStatusBar so FileManager can show how many bytes
worth of files are in the current directory.
  • Loading branch information
awesomekling committed Feb 10, 2019
1 parent 2cf1dd5 commit 2def3d8
Show file tree
Hide file tree
Showing 22 changed files with 411 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Applications/Clock/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
Expand Down
21 changes: 20 additions & 1 deletion Applications/FileManager/DirectoryView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ void DirectoryView::reload()
}
m_directories.clear();
m_files.clear();

size_t bytes_in_files = 0;
while (auto* de = readdir(dirp)) {
Entry entry;
entry.name = de->d_name;
Expand All @@ -66,10 +68,21 @@ void DirectoryView::reload()
entry.mode = st.st_mode;
auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files;
entries.append(move(entry));

if (S_ISREG(entry.mode))
bytes_in_files += st.st_size;
}
closedir(dirp);
int excess_height = max(0, (item_count() * item_height()) - height());
m_scrollbar->set_range(0, excess_height);



set_status_message(String::format("%d item%s (%u byte%s)",
item_count(),
item_count() != 1 ? "s" : "",
bytes_in_files,
bytes_in_files != 1 ? "s" : ""));
}

const GraphicsBitmap& DirectoryView::icon_for(const Entry& entry) const
Expand Down Expand Up @@ -128,7 +141,7 @@ void DirectoryView::paint_event(GPaintEvent&)
Rect icon_rect(horizontal_padding, y, icon_size, item_height());
Rect name_rect(icon_rect.right() + horizontal_padding, y, 100, item_height());
Rect size_rect(name_rect.right() + horizontal_padding, y, 64, item_height());
painter.fill_rect(row_rect(painted_item_index), i % 2 ? Color::LightGray : Color::White);
painter.fill_rect(row_rect(painted_item_index), i % 2 ? Color(210, 210, 210) : Color::White);
painter.blit_with_alpha(icon_rect.location(), icon_for(entry), { 0, 0, icon_size, icon_size });
painter.draw_text(name_rect, entry.name, Painter::TextAlignment::CenterLeft, Color::Black);
if (should_show_size_for(entry))
Expand All @@ -143,3 +156,9 @@ void DirectoryView::paint_event(GPaintEvent&)
unpainted_rect.intersect(rect());
painter.fill_rect(unpainted_rect, Color::White);
}

void DirectoryView::set_status_message(String&& message)
{
if (on_status_message)
on_status_message(move(message));
}
3 changes: 3 additions & 0 deletions Applications/FileManager/DirectoryView.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class DirectoryView final : public GWidget {
void reload();

Function<void(const String&)> on_path_change;
Function<void(String)> on_status_message;

int item_height() const { return 16; }
int item_count() const { return m_directories.size() + m_files.size(); }
Expand All @@ -24,6 +25,8 @@ class DirectoryView final : public GWidget {
virtual void resize_event(GResizeEvent&) override;
virtual void mousedown_event(GMouseEvent&) override;

void set_status_message(String&&);

Rect row_rect(int item_index) const;

struct Entry {
Expand Down
2 changes: 1 addition & 1 deletion Applications/FileManager/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
Expand Down
18 changes: 11 additions & 7 deletions Applications/FileManager/main.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
#include <SharedGraphics/GraphicsBitmap.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GListBox.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GEventLoop.h>
#include <sys/wait.h>
#include <signal.h>
#include <LibGUI/GStatusBar.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include "DirectoryView.h"

static GWindow* make_window();
Expand All @@ -33,13 +29,21 @@ GWindow* make_window()
auto* widget = new GWidget;
window->set_main_widget(widget);

widget->set_layout(make<GBoxLayout>(Orientation::Vertical));

auto* directory_view = new DirectoryView(widget);
directory_view->set_relative_rect({ 0, 0, 240, 300 });

auto* statusbar = new GStatusBar(widget);
statusbar->set_text("Welcome!");

directory_view->on_path_change = [window] (const String& new_path) {
window->set_title(String::format("FileManager: %s", new_path.characters()));
};

directory_view->on_status_message = [statusbar] (String message) {
statusbar->set_text(move(message));
};

directory_view->open("/");

return window;
Expand Down
2 changes: 1 addition & 1 deletion Applications/FontEditor/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
Expand Down
2 changes: 1 addition & 1 deletion Applications/Launcher/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
Expand Down
2 changes: 1 addition & 1 deletion Applications/Terminal/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibC/LibC.a
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
Expand Down
20 changes: 10 additions & 10 deletions Kernel/makeall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ make -C ../LibGUI clean && \
make -C ../LibGUI && \
make -C ../Userland clean && \
make -C ../Userland && \
make -C ../Application/Terminal clean && \
make -C ../Application/Terminal && \
make -C ../Application/FontEditor clean && \
make -C ../Application/FontEditor && \
make -C ../Application/Clock clean && \
make -C ../Application/Clock && \
make -C ../Application/Launcher clean && \
make -C ../Application/Launcher && \
make -C ../Application/FileManager clean && \
make -C ../Application/FileManager && \
make -C ../Applications/Terminal clean && \
make -C ../Applications/Terminal && \
make -C ../Applications/FontEditor clean && \
make -C ../Applications/FontEditor && \
make -C ../Applications/Clock clean && \
make -C ../Applications/Clock && \
make -C ../Applications/Launcher clean && \
make -C ../Applications/Launcher && \
make -C ../Applications/FileManager clean && \
make -C ../Applications/FileManager && \
make clean &&\
make && \
sudo ./sync.sh
Expand Down
6 changes: 6 additions & 0 deletions LibC/entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ extern "C" int _start()
// Birger's birthday <3
return 20150614;
}

extern "C" void __cxa_pure_virtual() NORETURN;
extern "C" void __cxa_pure_virtual()
{
ASSERT_NOT_REACHED();
}
89 changes: 89 additions & 0 deletions LibGUI/GBoxLayout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GWidget.h>

GBoxLayout::GBoxLayout(Orientation orientation)
: m_orientation(orientation)
{
}

GBoxLayout::~GBoxLayout()
{
}

#if 0
Size GLayout::compute_preferred_size() const
{

}


static Size compute_preferred_size(GLayout::Entry& entry)
{
if (entry.layout)
return entry.layout->compute_preferred_size();
else {
return entry.widget->preferred_size();
}
}
#endif

void GBoxLayout::run(GWidget& widget)
{
if (m_entries.is_empty())
return;

Size available_size = widget.size();
int number_of_entries_with_fixed_size = 0;

for (auto& entry : m_entries) {
if (entry.widget && entry.widget->size_policy(orientation()) == SizePolicy::Fixed) {
available_size -= entry.widget->preferred_size();
++number_of_entries_with_fixed_size;
}
}

int number_of_entries_with_automatic_size = m_entries.size() - number_of_entries_with_fixed_size;

dbgprintf("GBoxLayout: available_size=%d, fixed=%d, fill=%d\n", available_size.height(), number_of_entries_with_fixed_size, number_of_entries_with_automatic_size);

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);
}

dbgprintf("GBoxLayout: automatic_size=%s\n", automatic_size.to_string().characters());

int current_x = 0;
int current_y = 0;
for (auto& entry : m_entries) {
Rect rect(current_x, current_y, 0, 0);
if (entry.layout) {
// FIXME: Implement recursive layout.
ASSERT_NOT_REACHED();
}
ASSERT(entry.widget);
if (entry.widget->size_policy(orientation()) == SizePolicy::Fixed) {
rect.set_size(automatic_size);
if (orientation() == Orientation::Vertical) {
rect.set_height(entry.widget->preferred_size().height());
} else {
rect.set_width(entry.widget->preferred_size().height());
}
} else {
rect.set_size(automatic_size);
}

dbgprintf("GBoxLayout: apply, %s{%p} <- %s\n", entry.widget->class_name(), entry.widget.ptr(), rect.to_string().characters());
entry.widget->set_relative_rect(rect);

if (orientation() == Orientation::Horizontal)
current_x += rect.width();
else
current_y += rect.height();
}
}
18 changes: 18 additions & 0 deletions LibGUI/GBoxLayout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <LibGUI/GLayout.h>
#include <LibGUI/GWidget.h>

class GBoxLayout final : public GLayout {
public:
explicit GBoxLayout(Orientation);
virtual ~GBoxLayout() override;

Orientation orientation() const { return m_orientation; }

virtual void run(GWidget&) override;

private:
Orientation m_orientation;
};

41 changes: 41 additions & 0 deletions LibGUI/GLayout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <LibGUI/GLayout.h>
#include <LibGUI/GWidget.h>

GLayout::GLayout()
{
}

GLayout::~GLayout()
{
}

void GLayout::notify_adopted(Badge<GWidget>, GWidget& widget)
{
if (m_owner.ptr() == &widget)
return;
m_owner = widget.make_weak_ptr();
}

void GLayout::notify_disowned(Badge<GWidget>, GWidget& widget)
{
ASSERT(m_owner.ptr() == &widget);
m_owner.clear();
}

void GLayout::add_layout(OwnPtr<GLayout>&& layout)
{
Entry entry;
entry.layout = move(layout);
m_entries.append(move(entry));
if (m_owner)
m_owner->notify_layout_changed(Badge<GLayout>());
}

void GLayout::add_widget(GWidget& widget)
{
Entry entry;
entry.widget = widget.make_weak_ptr();
m_entries.append(move(entry));
if (m_owner)
m_owner->notify_layout_changed(Badge<GLayout>());
}
31 changes: 31 additions & 0 deletions LibGUI/GLayout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <AK/Badge.h>
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#include <AK/WeakPtr.h>

class GWidget;

class GLayout {
public:
GLayout();
virtual ~GLayout();

void add_widget(GWidget&);
void add_layout(OwnPtr<GLayout>&&);

virtual void run(GWidget&) = 0;

void notify_adopted(Badge<GWidget>, GWidget&);
void notify_disowned(Badge<GWidget>, GWidget&);

protected:
struct Entry {
WeakPtr<GWidget> widget;
OwnPtr<GLayout> layout;
};
WeakPtr<GWidget> m_owner;
Vector<Entry> m_entries;
};

Loading

0 comments on commit 2def3d8

Please sign in to comment.