mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:42:43 +00:00 
			
		
		
		
	LibGUI: Start adding an automatic widget layout system.
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.
This commit is contained in:
		
							parent
							
								
									2cf1dd5b6f
								
							
						
					
					
						commit
						2def3d8d3f
					
				
					 22 changed files with 411 additions and 29 deletions
				
			
		|  | @ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections | ||||||
| all: $(APP) | all: $(APP) | ||||||
| 
 | 
 | ||||||
| $(APP): $(OBJS) | $(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: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
|  | @ -53,6 +53,8 @@ void DirectoryView::reload() | ||||||
|     } |     } | ||||||
|     m_directories.clear(); |     m_directories.clear(); | ||||||
|     m_files.clear(); |     m_files.clear(); | ||||||
|  | 
 | ||||||
|  |     size_t bytes_in_files = 0; | ||||||
|     while (auto* de = readdir(dirp)) { |     while (auto* de = readdir(dirp)) { | ||||||
|         Entry entry; |         Entry entry; | ||||||
|         entry.name = de->d_name; |         entry.name = de->d_name; | ||||||
|  | @ -66,10 +68,21 @@ void DirectoryView::reload() | ||||||
|         entry.mode = st.st_mode; |         entry.mode = st.st_mode; | ||||||
|         auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files; |         auto& entries = S_ISDIR(st.st_mode) ? m_directories : m_files; | ||||||
|         entries.append(move(entry)); |         entries.append(move(entry)); | ||||||
|  | 
 | ||||||
|  |         if (S_ISREG(entry.mode)) | ||||||
|  |             bytes_in_files += st.st_size; | ||||||
|     } |     } | ||||||
|     closedir(dirp); |     closedir(dirp); | ||||||
|     int excess_height = max(0, (item_count() * item_height()) - height()); |     int excess_height = max(0, (item_count() * item_height()) - height()); | ||||||
|     m_scrollbar->set_range(0, excess_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 | const GraphicsBitmap& DirectoryView::icon_for(const Entry& entry) const | ||||||
|  | @ -128,7 +141,7 @@ void DirectoryView::paint_event(GPaintEvent&) | ||||||
|             Rect icon_rect(horizontal_padding, y, icon_size, item_height()); |             Rect icon_rect(horizontal_padding, y, icon_size, item_height()); | ||||||
|             Rect name_rect(icon_rect.right() + horizontal_padding, y, 100, 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()); |             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.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); |             painter.draw_text(name_rect, entry.name, Painter::TextAlignment::CenterLeft, Color::Black); | ||||||
|             if (should_show_size_for(entry)) |             if (should_show_size_for(entry)) | ||||||
|  | @ -143,3 +156,9 @@ void DirectoryView::paint_event(GPaintEvent&) | ||||||
|     unpainted_rect.intersect(rect()); |     unpainted_rect.intersect(rect()); | ||||||
|     painter.fill_rect(unpainted_rect, Color::White); |     painter.fill_rect(unpainted_rect, Color::White); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void DirectoryView::set_status_message(String&& message) | ||||||
|  | { | ||||||
|  |     if (on_status_message) | ||||||
|  |         on_status_message(move(message)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ public: | ||||||
|     void reload(); |     void reload(); | ||||||
| 
 | 
 | ||||||
|     Function<void(const String&)> on_path_change; |     Function<void(const String&)> on_path_change; | ||||||
|  |     Function<void(String)> on_status_message; | ||||||
| 
 | 
 | ||||||
|     int item_height() const { return 16; } |     int item_height() const { return 16; } | ||||||
|     int item_count() const { return m_directories.size() + m_files.size(); } |     int item_count() const { return m_directories.size() + m_files.size(); } | ||||||
|  | @ -24,6 +25,8 @@ private: | ||||||
|     virtual void resize_event(GResizeEvent&) override; |     virtual void resize_event(GResizeEvent&) override; | ||||||
|     virtual void mousedown_event(GMouseEvent&) override; |     virtual void mousedown_event(GMouseEvent&) override; | ||||||
| 
 | 
 | ||||||
|  |     void set_status_message(String&&); | ||||||
|  | 
 | ||||||
|     Rect row_rect(int item_index) const; |     Rect row_rect(int item_index) const; | ||||||
| 
 | 
 | ||||||
|     struct Entry { |     struct Entry { | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections | ||||||
| all: $(APP) | all: $(APP) | ||||||
| 
 | 
 | ||||||
| $(APP): $(OBJS) | $(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: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
|  | @ -1,14 +1,10 @@ | ||||||
| #include <SharedGraphics/GraphicsBitmap.h> |  | ||||||
| #include <LibGUI/GWindow.h> | #include <LibGUI/GWindow.h> | ||||||
| #include <LibGUI/GWidget.h> | #include <LibGUI/GWidget.h> | ||||||
| #include <LibGUI/GButton.h> | #include <LibGUI/GBoxLayout.h> | ||||||
| #include <LibGUI/GListBox.h> |  | ||||||
| #include <LibGUI/GEventLoop.h> | #include <LibGUI/GEventLoop.h> | ||||||
| #include <sys/wait.h> | #include <LibGUI/GStatusBar.h> | ||||||
| #include <signal.h> |  | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <errno.h> |  | ||||||
| #include "DirectoryView.h" | #include "DirectoryView.h" | ||||||
| 
 | 
 | ||||||
| static GWindow* make_window(); | static GWindow* make_window(); | ||||||
|  | @ -33,13 +29,21 @@ GWindow* make_window() | ||||||
|     auto* widget = new GWidget; |     auto* widget = new GWidget; | ||||||
|     window->set_main_widget(widget); |     window->set_main_widget(widget); | ||||||
| 
 | 
 | ||||||
|  |     widget->set_layout(make<GBoxLayout>(Orientation::Vertical)); | ||||||
|  | 
 | ||||||
|     auto* directory_view = new DirectoryView(widget); |     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) { |     directory_view->on_path_change = [window] (const String& new_path) { | ||||||
|         window->set_title(String::format("FileManager: %s", new_path.characters())); |         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("/"); |     directory_view->open("/"); | ||||||
| 
 | 
 | ||||||
|     return window; |     return window; | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections | ||||||
| all: $(APP) | all: $(APP) | ||||||
| 
 | 
 | ||||||
| $(APP): $(OBJS) | $(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: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections | ||||||
| all: $(APP) | all: $(APP) | ||||||
| 
 | 
 | ||||||
| $(APP): $(OBJS) | $(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: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections | ||||||
| all: $(APP) | all: $(APP) | ||||||
| 
 | 
 | ||||||
| $(APP): $(OBJS) | $(APP): $(OBJS) | ||||||
| 	$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibC/LibC.a | 	$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibC/LibC.a | ||||||
| 
 | 
 | ||||||
| .cpp.o: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
|  | @ -8,16 +8,16 @@ make -C ../LibGUI clean && \ | ||||||
| make -C ../LibGUI && \ | make -C ../LibGUI && \ | ||||||
| make -C ../Userland clean && \ | make -C ../Userland clean && \ | ||||||
| make -C ../Userland && \ | make -C ../Userland && \ | ||||||
| make -C ../Application/Terminal clean && \ | make -C ../Applications/Terminal clean && \ | ||||||
| make -C ../Application/Terminal && \ | make -C ../Applications/Terminal && \ | ||||||
| make -C ../Application/FontEditor clean && \ | make -C ../Applications/FontEditor clean && \ | ||||||
| make -C ../Application/FontEditor && \ | make -C ../Applications/FontEditor && \ | ||||||
| make -C ../Application/Clock clean && \ | make -C ../Applications/Clock clean && \ | ||||||
| make -C ../Application/Clock && \ | make -C ../Applications/Clock && \ | ||||||
| make -C ../Application/Launcher clean && \ | make -C ../Applications/Launcher clean && \ | ||||||
| make -C ../Application/Launcher && \ | make -C ../Applications/Launcher && \ | ||||||
| make -C ../Application/FileManager clean && \ | make -C ../Applications/FileManager clean && \ | ||||||
| make -C ../Application/FileManager && \ | make -C ../Applications/FileManager && \ | ||||||
| make clean &&\ | make clean &&\ | ||||||
| make && \ | make && \ | ||||||
| sudo ./sync.sh | sudo ./sync.sh | ||||||
|  |  | ||||||
|  | @ -38,3 +38,9 @@ epilogue: | ||||||
|     // Birger's birthday <3
 |     // Birger's birthday <3
 | ||||||
|     return 20150614; |     return 20150614; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | extern "C" void __cxa_pure_virtual() NORETURN; | ||||||
|  | extern "C" void __cxa_pure_virtual() | ||||||
|  | { | ||||||
|  |     ASSERT_NOT_REACHED(); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								LibGUI/GBoxLayout.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								LibGUI/GBoxLayout.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
									
								
								LibGUI/GBoxLayout.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								LibGUI/GBoxLayout.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
									
								
								LibGUI/GLayout.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								LibGUI/GLayout.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
									
								
								LibGUI/GLayout.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								LibGUI/GLayout.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
							
								
								
									
										35
									
								
								LibGUI/GStatusBar.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								LibGUI/GStatusBar.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | #include <LibGUI/GStatusBar.h> | ||||||
|  | #include <LibGUI/GLabel.h> | ||||||
|  | #include <LibGUI/GBoxLayout.h> | ||||||
|  | #include <SharedGraphics/Painter.h> | ||||||
|  | 
 | ||||||
|  | GStatusBar::GStatusBar(GWidget* parent) | ||||||
|  |     : GWidget(parent) | ||||||
|  | { | ||||||
|  |     set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); | ||||||
|  |     set_preferred_size({ 0, 16 }); | ||||||
|  |     set_layout(make<GBoxLayout>(Orientation::Horizontal)); | ||||||
|  |     m_label = new GLabel(this); | ||||||
|  |     m_label->set_fill_with_background_color(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GStatusBar::~GStatusBar() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GStatusBar::set_text(String&& text) | ||||||
|  | { | ||||||
|  |     m_label->set_text(move(text)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String GStatusBar::text() const | ||||||
|  | { | ||||||
|  |     return m_label->text(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GStatusBar::paint_event(GPaintEvent&) | ||||||
|  | { | ||||||
|  |     Painter painter(*this); | ||||||
|  |     painter.fill_rect({ 0, 1, width(), height() - 1 }, Color::LightGray); | ||||||
|  |     painter.draw_line({ 0, 0 }, { width() - 1, 0 }, Color::DarkGray); | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								LibGUI/GStatusBar.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								LibGUI/GStatusBar.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibGUI/GWidget.h> | ||||||
|  | 
 | ||||||
|  | class GLabel; | ||||||
|  | 
 | ||||||
|  | class GStatusBar : public GWidget { | ||||||
|  | public: | ||||||
|  |     explicit GStatusBar(GWidget* parent); | ||||||
|  |     virtual ~GStatusBar() override; | ||||||
|  | 
 | ||||||
|  |     String text() const; | ||||||
|  |     void set_text(String&&); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     virtual const char* class_name() const override { return "GStatusBar"; } | ||||||
|  |     virtual void paint_event(GPaintEvent&) override; | ||||||
|  | 
 | ||||||
|  |     GLabel* m_label { nullptr }; | ||||||
|  | }; | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| #include "GEvent.h" | #include "GEvent.h" | ||||||
| #include "GEventLoop.h" | #include "GEventLoop.h" | ||||||
| #include "GWindow.h" | #include "GWindow.h" | ||||||
|  | #include <LibGUI/GLayout.h> | ||||||
| #include <AK/Assertions.h> | #include <AK/Assertions.h> | ||||||
| #include <SharedGraphics/GraphicsBitmap.h> | #include <SharedGraphics/GraphicsBitmap.h> | ||||||
| #include <SharedGraphics/Painter.h> | #include <SharedGraphics/Painter.h> | ||||||
|  | @ -12,6 +13,9 @@ GWidget::GWidget(GWidget* parent) | ||||||
|     set_font(nullptr); |     set_font(nullptr); | ||||||
|     m_background_color = Color::LightGray; |     m_background_color = Color::LightGray; | ||||||
|     m_foreground_color = Color::Black; |     m_foreground_color = Color::Black; | ||||||
|  | 
 | ||||||
|  |     if (parent && parent->layout()) | ||||||
|  |         parent->layout()->add_widget(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GWidget::~GWidget() | GWidget::~GWidget() | ||||||
|  | @ -42,7 +46,7 @@ void GWidget::event(GEvent& event) | ||||||
|         m_has_pending_paint_event = false; |         m_has_pending_paint_event = false; | ||||||
|         return handle_paint_event(static_cast<GPaintEvent&>(event)); |         return handle_paint_event(static_cast<GPaintEvent&>(event)); | ||||||
|     case GEvent::Resize: |     case GEvent::Resize: | ||||||
|         return resize_event(static_cast<GResizeEvent&>(event)); |         return handle_resize_event(static_cast<GResizeEvent&>(event)); | ||||||
|     case GEvent::FocusIn: |     case GEvent::FocusIn: | ||||||
|         return focusin_event(event); |         return focusin_event(event); | ||||||
|     case GEvent::FocusOut: |     case GEvent::FocusOut: | ||||||
|  | @ -87,6 +91,41 @@ void GWidget::handle_paint_event(GPaintEvent& event) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GWidget::set_layout(OwnPtr<GLayout>&& layout) | ||||||
|  | { | ||||||
|  |     if (m_layout.ptr() == layout.ptr()) | ||||||
|  |         return; | ||||||
|  |     if (m_layout) | ||||||
|  |         m_layout->notify_disowned(Badge<GWidget>(), *this); | ||||||
|  |     m_layout = move(layout); | ||||||
|  |     if (m_layout) { | ||||||
|  |         m_layout->notify_adopted(Badge<GWidget>(), *this); | ||||||
|  |         do_layout(); | ||||||
|  |     } else { | ||||||
|  |         update(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWidget::do_layout() | ||||||
|  | { | ||||||
|  |     if (!m_layout) | ||||||
|  |         return; | ||||||
|  |     m_layout->run(*this); | ||||||
|  |     update(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWidget::notify_layout_changed(Badge<GLayout>) | ||||||
|  | { | ||||||
|  |     do_layout(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWidget::handle_resize_event(GResizeEvent& event) | ||||||
|  | { | ||||||
|  |     if (layout()) | ||||||
|  |         do_layout(); | ||||||
|  |     return resize_event(event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GWidget::resize_event(GResizeEvent&) | void GWidget::resize_event(GResizeEvent&) | ||||||
| { | { | ||||||
| } | } | ||||||
|  | @ -216,3 +255,30 @@ bool GWidget::global_cursor_tracking() const | ||||||
|         return false; |         return false; | ||||||
|     return win->global_cursor_tracking_widget() == this; |     return win->global_cursor_tracking_widget() == this; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void GWidget::set_preferred_size(const Size& size) | ||||||
|  | { | ||||||
|  |     if (m_preferred_size == size) | ||||||
|  |         return; | ||||||
|  |     m_preferred_size = size; | ||||||
|  |     invalidate_layout(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWidget::set_size_policy(SizePolicy horizontal_policy, SizePolicy vertical_policy) | ||||||
|  | { | ||||||
|  |     if (m_horizontal_size_policy == horizontal_policy && m_vertical_size_policy == vertical_policy) | ||||||
|  |         return; | ||||||
|  |     m_horizontal_size_policy = horizontal_policy; | ||||||
|  |     m_vertical_size_policy = vertical_policy; | ||||||
|  |     invalidate_layout(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWidget::invalidate_layout() | ||||||
|  | { | ||||||
|  |     auto* w = window(); | ||||||
|  |     if (!w) | ||||||
|  |         return; | ||||||
|  |     if (!w->main_widget()) | ||||||
|  |         return; | ||||||
|  |     w->main_widget()->do_layout(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -5,16 +5,32 @@ | ||||||
| #include <SharedGraphics/Rect.h> | #include <SharedGraphics/Rect.h> | ||||||
| #include <SharedGraphics/Color.h> | #include <SharedGraphics/Color.h> | ||||||
| #include <SharedGraphics/Font.h> | #include <SharedGraphics/Font.h> | ||||||
|  | #include <AK/Badge.h> | ||||||
| #include <AK/AKString.h> | #include <AK/AKString.h> | ||||||
| 
 | 
 | ||||||
| class GraphicsBitmap; | class GraphicsBitmap; | ||||||
|  | class GLayout; | ||||||
| class GWindow; | class GWindow; | ||||||
| 
 | 
 | ||||||
|  | enum class SizePolicy { Fixed, Fill }; | ||||||
|  | enum class Orientation { Horizontal, Vertical }; | ||||||
|  | 
 | ||||||
| class GWidget : public GObject { | class GWidget : public GObject { | ||||||
| public: | public: | ||||||
|     explicit GWidget(GWidget* parent = nullptr); |     explicit GWidget(GWidget* parent = nullptr); | ||||||
|     virtual ~GWidget() override; |     virtual ~GWidget() override; | ||||||
| 
 | 
 | ||||||
|  |     GLayout* layout() { return m_layout.ptr(); } | ||||||
|  |     void set_layout(OwnPtr<GLayout>&&); | ||||||
|  | 
 | ||||||
|  |     SizePolicy horizontal_size_policy() const { return m_horizontal_size_policy; } | ||||||
|  |     SizePolicy vertical_size_policy() const { return m_vertical_size_policy; } | ||||||
|  |     SizePolicy size_policy(Orientation orientation) { return orientation == Orientation::Horizontal ? m_horizontal_size_policy : m_vertical_size_policy; } | ||||||
|  |     void set_size_policy(SizePolicy horizontal_policy, SizePolicy vertical_policy); | ||||||
|  | 
 | ||||||
|  |     Size preferred_size() const { return m_preferred_size; } | ||||||
|  |     void set_preferred_size(const Size&); | ||||||
|  | 
 | ||||||
|     virtual void event(GEvent&) override; |     virtual void event(GEvent&) override; | ||||||
|     virtual void paint_event(GPaintEvent&); |     virtual void paint_event(GPaintEvent&); | ||||||
|     virtual void resize_event(GResizeEvent&); |     virtual void resize_event(GResizeEvent&); | ||||||
|  | @ -100,16 +116,26 @@ public: | ||||||
|     void set_global_cursor_tracking(bool); |     void set_global_cursor_tracking(bool); | ||||||
|     bool global_cursor_tracking() const; |     bool global_cursor_tracking() const; | ||||||
| 
 | 
 | ||||||
|  |     void notify_layout_changed(Badge<GLayout>); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     void handle_paint_event(GPaintEvent&); |     void handle_paint_event(GPaintEvent&); | ||||||
|  |     void handle_resize_event(GResizeEvent&); | ||||||
|  |     void do_layout(); | ||||||
|  |     void invalidate_layout(); | ||||||
| 
 | 
 | ||||||
|     GWindow* m_window { nullptr }; |     GWindow* m_window { nullptr }; | ||||||
|  |     OwnPtr<GLayout> m_layout; | ||||||
| 
 | 
 | ||||||
|     Rect m_relative_rect; |     Rect m_relative_rect; | ||||||
|     Color m_background_color { 0xffffff }; |     Color m_background_color { 0xffffff }; | ||||||
|     Color m_foreground_color { 0x000000 }; |     Color m_foreground_color { 0x000000 }; | ||||||
|     RetainPtr<Font> m_font; |     RetainPtr<Font> m_font; | ||||||
| 
 | 
 | ||||||
|  |     SizePolicy m_horizontal_size_policy { SizePolicy::Fill }; | ||||||
|  |     SizePolicy m_vertical_size_policy { SizePolicy::Fill }; | ||||||
|  |     Size m_preferred_size; | ||||||
|  | 
 | ||||||
|     bool m_has_pending_paint_event { false }; |     bool m_has_pending_paint_event { false }; | ||||||
|     bool m_fill_with_background_color { true }; |     bool m_fill_with_background_color { true }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -15,8 +15,11 @@ LIBGUI_OBJS = \ | ||||||
|     GObject.o \
 |     GObject.o \
 | ||||||
|     GTextBox.o \
 |     GTextBox.o \
 | ||||||
|     GScrollBar.o \
 |     GScrollBar.o \
 | ||||||
|  |     GStatusBar.o \
 | ||||||
|     GWidget.o \
 |     GWidget.o \
 | ||||||
|     GStyle.o \
 |     GStyle.o \
 | ||||||
|  |     GLayout.o \
 | ||||||
|  |     GBoxLayout.o \
 | ||||||
|     GWindow.o |     GWindow.o | ||||||
| 
 | 
 | ||||||
| OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS) | OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS) | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| #include <AK/Assertions.h> | #include <AK/Assertions.h> | ||||||
| #include <AK/StdLibExtras.h> | #include <AK/StdLibExtras.h> | ||||||
| 
 | 
 | ||||||
| #ifdef LIBGUI | #ifdef USERLAND | ||||||
| #include <LibGUI/GWidget.h> | #include <LibGUI/GWidget.h> | ||||||
| #include <LibGUI/GWindow.h> | #include <LibGUI/GWindow.h> | ||||||
| #include <LibC/gui.h> | #include <LibC/gui.h> | ||||||
|  | @ -20,7 +20,7 @@ Painter::Painter(GraphicsBitmap& bitmap) | ||||||
|     m_clip_rect = { { 0, 0 }, bitmap.size() }; |     m_clip_rect = { { 0, 0 }, bitmap.size() }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef LIBGUI | #ifdef USERLAND | ||||||
| Painter::Painter(GWidget& widget) | Painter::Painter(GWidget& widget) | ||||||
|     : m_font(&widget.font()) |     : m_font(&widget.font()) | ||||||
| { | { | ||||||
|  | @ -34,9 +34,9 @@ Painter::Painter(GWidget& widget) | ||||||
|     m_target = GraphicsBitmap::create_wrapper(backing.size, backing.pixels); |     m_target = GraphicsBitmap::create_wrapper(backing.size, backing.pixels); | ||||||
|     ASSERT(m_target); |     ASSERT(m_target); | ||||||
|     m_window = widget.window(); |     m_window = widget.window(); | ||||||
|     m_translation.move_by(widget.relative_position()); |     m_translation.move_by(widget.window_relative_rect().location()); | ||||||
|     // NOTE: m_clip_rect is in Window coordinates since we are painting into its backing store.
 |     // NOTE: m_clip_rect is in Window coordinates since we are painting into its backing store.
 | ||||||
|     m_clip_rect = widget.relative_rect(); |     m_clip_rect = widget.window_relative_rect(); | ||||||
|     m_clip_rect.intersect(m_target->rect()); |     m_clip_rect.intersect(m_target->rect()); | ||||||
| 
 | 
 | ||||||
| #ifdef DEBUG_WIDGET_UNDERDRAW | #ifdef DEBUG_WIDGET_UNDERDRAW | ||||||
|  | @ -49,7 +49,7 @@ Painter::Painter(GWidget& widget) | ||||||
| 
 | 
 | ||||||
| Painter::~Painter() | Painter::~Painter() | ||||||
| { | { | ||||||
| #ifdef LIBGUI | #ifdef USERLAND | ||||||
|     m_target = nullptr; |     m_target = nullptr; | ||||||
|     int rc = gui_release_window_backing_store(m_backing_store_id); |     int rc = gui_release_window_backing_store(m_backing_store_id); | ||||||
|     ASSERT(rc == 0); |     ASSERT(rc == 0); | ||||||
|  |  | ||||||
|  | @ -52,6 +52,16 @@ public: | ||||||
|         return { x() + width() / 2, y() + height() / 2 }; |         return { x() + width() / 2, y() + height() / 2 }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void set_location(const Point& location) | ||||||
|  |     { | ||||||
|  |         m_location = location; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void set_size(const Size& size) | ||||||
|  |     { | ||||||
|  |         m_size = size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void inflate(int w, int h) |     void inflate(int w, int h) | ||||||
|     { |     { | ||||||
|         set_x(x() - w / 2); |         set_x(x() - w / 2); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <AK/AKString.h> | ||||||
|  | 
 | ||||||
| struct GUI_Size; | struct GUI_Size; | ||||||
| 
 | 
 | ||||||
| class Size { | class Size { | ||||||
|  | @ -29,8 +31,17 @@ public: | ||||||
|         return !(*this == other); |         return !(*this == other); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Size& operator-=(const Size& other) | ||||||
|  |     { | ||||||
|  |         m_width -= other.m_width; | ||||||
|  |         m_height -= other.m_height; | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     operator GUI_Size() const; |     operator GUI_Size() const; | ||||||
| 
 | 
 | ||||||
|  |     String to_string() const { return String::format("[%d,%d]", m_width, m_height); } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     int m_width { 0 }; |     int m_width { 0 }; | ||||||
|     int m_height { 0 }; |     int m_height { 0 }; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling