mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	More work on the variable-width font support.
Katica is now the default system font, and it looks quite nice. :^) I'm gonna need to refine the GTextBox movement stuff eventually, but it works well-enough for basic editing now.
This commit is contained in:
		
							parent
							
								
									e53cef02d5
								
							
						
					
					
						commit
						66a5ddd94a
					
				
					 12 changed files with 125 additions and 30 deletions
				
			
		|  | @ -19,9 +19,9 @@ | ||||||
| 
 | 
 | ||||||
| Terminal::Terminal(int ptm_fd) | Terminal::Terminal(int ptm_fd) | ||||||
|     : m_ptm_fd(ptm_fd) |     : m_ptm_fd(ptm_fd) | ||||||
|     , m_font(Font::default_font()) |  | ||||||
|     , m_notifier(ptm_fd, GNotifier::Read) |     , m_notifier(ptm_fd, GNotifier::Read) | ||||||
| { | { | ||||||
|  |     set_font(Font::default_fixed_width_font()); | ||||||
|     m_notifier.on_ready_to_read = [this] (GNotifier& notifier) { |     m_notifier.on_ready_to_read = [this] (GNotifier& notifier) { | ||||||
|         byte buffer[BUFSIZ]; |         byte buffer[BUFSIZ]; | ||||||
|         ssize_t nread = read(notifier.fd(), buffer, sizeof(buffer)); |         ssize_t nread = read(notifier.fd(), buffer, sizeof(buffer)); | ||||||
|  | @ -798,7 +798,7 @@ void Terminal::force_repaint() | ||||||
| 
 | 
 | ||||||
| void Terminal::resize_event(GResizeEvent& event) | void Terminal::resize_event(GResizeEvent& event) | ||||||
| { | { | ||||||
|     int new_columns = event.size().width() / m_font->glyph_width('x'); |     int new_columns = event.size().width() / font().glyph_width('x'); | ||||||
|     int new_rows = event.size().height() / m_line_height; |     int new_rows = event.size().height() / m_line_height; | ||||||
|     set_size(new_columns, new_rows); |     set_size(new_columns, new_rows); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -30,7 +30,6 @@ private: | ||||||
|     virtual void keydown_event(GKeyEvent&) override; |     virtual void keydown_event(GKeyEvent&) override; | ||||||
|     virtual const char* class_name() const override { return "Terminal"; } |     virtual const char* class_name() const override { return "Terminal"; } | ||||||
| 
 | 
 | ||||||
|     Font& font() { return *m_font; } |  | ||||||
|     void scroll_up(); |     void scroll_up(); | ||||||
|     void newline(); |     void newline(); | ||||||
|     void set_cursor(unsigned row, unsigned column); |     void set_cursor(unsigned row, unsigned column); | ||||||
|  | @ -145,8 +144,6 @@ private: | ||||||
|     bool m_in_active_window { false }; |     bool m_in_active_window { false }; | ||||||
|     bool m_need_full_flush { false }; |     bool m_need_full_flush { false }; | ||||||
| 
 | 
 | ||||||
|     RetainPtr<Font> m_font; |  | ||||||
| 
 |  | ||||||
|     GNotifier m_notifier; |     GNotifier m_notifier; | ||||||
| 
 | 
 | ||||||
|     float m_opacity { 0.8f }; |     float m_opacity { 0.8f }; | ||||||
|  |  | ||||||
|  | @ -108,7 +108,7 @@ int main(int argc, char** argv) | ||||||
|     menubar->add_menu(move(app_menu)); |     menubar->add_menu(move(app_menu)); | ||||||
| 
 | 
 | ||||||
|     auto font_menu = make<GMenu>("Font"); |     auto font_menu = make<GMenu>("Font"); | ||||||
|     GFontDatabase::the().for_each_font([&] (const String& font_name) { |     GFontDatabase::the().for_each_fixed_width_font([&] (const String& font_name) { | ||||||
|         font_menu->add_action(GAction::create(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.set_font(GFontDatabase::the().get_by_name(action.text())); | ||||||
|             terminal.force_repaint(); |             terminal.force_repaint(); | ||||||
|  |  | ||||||
|  | @ -24,8 +24,13 @@ GFontDatabase::GFontDatabase() | ||||||
|         if (de->d_name[0] == '.') |         if (de->d_name[0] == '.') | ||||||
|             continue; |             continue; | ||||||
|         auto path = String::format("/res/fonts/%s", de->d_name); |         auto path = String::format("/res/fonts/%s", de->d_name); | ||||||
|         if (auto font = Font::load_from_file(path)) |         if (auto font = Font::load_from_file(path)) { | ||||||
|             m_name_to_path.set(font->name(), path); |             Metadata metadata; | ||||||
|  |             metadata.path = path; | ||||||
|  |             metadata.glyph_height = font->glyph_height(); | ||||||
|  |             metadata.is_fixed_width = font->is_fixed_width(); | ||||||
|  |             m_name_to_metadata.set(font->name(), move(metadata)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     closedir(dirp); |     closedir(dirp); | ||||||
| } | } | ||||||
|  | @ -36,15 +41,23 @@ GFontDatabase::~GFontDatabase() | ||||||
| 
 | 
 | ||||||
| void GFontDatabase::for_each_font(Function<void(const String&)> callback) | void GFontDatabase::for_each_font(Function<void(const String&)> callback) | ||||||
| { | { | ||||||
|     for (auto& it : m_name_to_path) { |     for (auto& it : m_name_to_metadata) { | ||||||
|  |         callback(it.key); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GFontDatabase::for_each_fixed_width_font(Function<void(const String&)> callback) | ||||||
|  | { | ||||||
|  |     for (auto& it : m_name_to_metadata) { | ||||||
|  |         if (it.value.is_fixed_width) | ||||||
|             callback(it.key); |             callback(it.key); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RetainPtr<Font> GFontDatabase::get_by_name(const String& name) | RetainPtr<Font> GFontDatabase::get_by_name(const String& name) | ||||||
| { | { | ||||||
|     auto it = m_name_to_path.find(name); |     auto it = m_name_to_metadata.find(name); | ||||||
|     if (it == m_name_to_path.end()) |     if (it == m_name_to_metadata.end()) | ||||||
|         return nullptr; |         return nullptr; | ||||||
|     return Font::load_from_file((*it).value); |     return Font::load_from_file((*it).value.path); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,10 +12,17 @@ public: | ||||||
| 
 | 
 | ||||||
|     RetainPtr<Font> get_by_name(const String&); |     RetainPtr<Font> get_by_name(const String&); | ||||||
|     void for_each_font(Function<void(const String&)>); |     void for_each_font(Function<void(const String&)>); | ||||||
|  |     void for_each_fixed_width_font(Function<void(const String&)>); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     GFontDatabase(); |     GFontDatabase(); | ||||||
|     ~GFontDatabase(); |     ~GFontDatabase(); | ||||||
| 
 | 
 | ||||||
|     HashMap<String, String> m_name_to_path; |     struct Metadata { | ||||||
|  |         String path; | ||||||
|  |         bool is_fixed_width; | ||||||
|  |         int glyph_height; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     HashMap<String, Metadata> m_name_to_metadata; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -21,12 +21,58 @@ void GTextBox::set_text(const String& text) | ||||||
|         return; |         return; | ||||||
|     m_text = text; |     m_text = text; | ||||||
|     m_cursor_position = m_text.length(); |     m_cursor_position = m_text.length(); | ||||||
|  |     scroll_cursor_into_view(HorizontalDirection::Right); | ||||||
|     update(); |     update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GTextBox::scroll_cursor_into_view(HorizontalDirection direction) | ||||||
|  | { | ||||||
|  |     if (visible_content_rect().contains(cursor_content_position())) | ||||||
|  |         return; | ||||||
|  |     int total_text_width = font().width(m_text); | ||||||
|  |     dbgprintf("total_text_width = %d, visible_content width = %d\n", total_text_width, visible_content_rect().width()); | ||||||
|  |     if (total_text_width < visible_content_rect().width()) { | ||||||
|  |         m_scroll_offset = 0; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (direction == HorizontalDirection::Left) { | ||||||
|  |         dbgprintf("Left, orig offset = %d\n", m_scroll_offset); | ||||||
|  |         m_scroll_offset = cursor_content_position().x(); | ||||||
|  |         int offset_into_visible = m_scroll_offset - visible_content_rect().x(); | ||||||
|  |         if (offset_into_visible < font().glyph_width(' ')) | ||||||
|  |             m_scroll_offset -= width() / 2; | ||||||
|  |     } else { | ||||||
|  |         m_scroll_offset = cursor_content_position().x() - visible_content_rect().width(); | ||||||
|  |         dbgprintf("Right, orig offset = %d\n", m_scroll_offset); | ||||||
|  |         int offset_into_visible = m_scroll_offset - visible_content_rect().x(); | ||||||
|  |         if (offset_into_visible > width() / 4) { | ||||||
|  |             dbgprintf("Right, adjust offset = %d\n", m_scroll_offset); | ||||||
|  |             m_scroll_offset += width() / 2; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (m_scroll_offset < 0) | ||||||
|  |         m_scroll_offset = 0; | ||||||
|  |     if (m_scroll_offset > total_text_width)dbgprintf("Right, adjust offset = %d\n", m_scroll_offset); | ||||||
|  |         m_scroll_offset = total_text_width - width(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Rect GTextBox::visible_content_rect() const | ||||||
|  | { | ||||||
|  |     if (rect().is_empty()) | ||||||
|  |         return { }; | ||||||
|  |     return { m_scroll_offset, 0, rect().shrunken(6, 6).width(), rect().shrunken(6, 6).height() }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Point GTextBox::cursor_content_position() const | ||||||
|  | { | ||||||
|  |     int x = 0; | ||||||
|  |     for (int i = 0; i < m_cursor_position; ++i) | ||||||
|  |         x += font().glyph_width(m_text[i]) + font().glyph_spacing(); | ||||||
|  |     return { x, 0 }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GTextBox::paint_event(GPaintEvent& event) | void GTextBox::paint_event(GPaintEvent& event) | ||||||
| { | { | ||||||
|     ASSERT(font().is_fixed_width()); |  | ||||||
|     Painter painter(*this); |     Painter painter(*this); | ||||||
|     painter.set_clip_rect(event.rect()); |     painter.set_clip_rect(event.rect()); | ||||||
| 
 | 
 | ||||||
|  | @ -39,27 +85,25 @@ void GTextBox::paint_event(GPaintEvent& event) | ||||||
|     Rect inner_rect = rect(); |     Rect inner_rect = rect(); | ||||||
|     inner_rect.shrink(6, 6); |     inner_rect.shrink(6, 6); | ||||||
| 
 | 
 | ||||||
|     ssize_t max_chars_to_paint = inner_rect.width() / font().min_glyph_width(); |     painter.set_clip_rect(inner_rect); | ||||||
|  |     painter.translate(-m_scroll_offset, 0); | ||||||
| 
 | 
 | ||||||
|     int first_visible_char = max((int)m_cursor_position - (int)max_chars_to_paint, 0); |  | ||||||
|     ssize_t chars_to_paint = min(m_text.length() - first_visible_char, max_chars_to_paint); |  | ||||||
|     int y = inner_rect.center().y() - font().glyph_height() / 2;     |     int y = inner_rect.center().y() - font().glyph_height() / 2;     | ||||||
|     int space_width = font().glyph_width(' '); |     int space_width = font().glyph_width(' ') + font().glyph_spacing(); | ||||||
|     int x = inner_rect.x(); |     int x = inner_rect.x(); | ||||||
| 
 | 
 | ||||||
|     for (ssize_t i = 0; i < chars_to_paint; ++i) { |     for (int i = 0; i < m_text.length(); ++i) { | ||||||
|         char ch = m_text[first_visible_char + i]; |         char ch = m_text[i]; | ||||||
|         if (ch == ' ') { |         if (ch == ' ') { | ||||||
|             x += space_width; |             x += space_width; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         painter.draw_glyph({x, y}, ch, Color::Black); |         painter.draw_glyph({x, y}, ch, Color::Black); | ||||||
|         x += font().glyph_width(ch); |         x += font().glyph_width(ch) + font().glyph_spacing(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (is_focused() && m_cursor_blink_state) { |     if (is_focused() && m_cursor_blink_state) { | ||||||
|         int visible_cursor_position = m_cursor_position - first_visible_char; |         Rect cursor_rect(inner_rect.x() + cursor_content_position().x(), inner_rect.y(), 1, inner_rect.height()); | ||||||
|         Rect cursor_rect(inner_rect.x() + visible_cursor_position * font().glyph_width('x'), inner_rect.y(), 1, inner_rect.height()); |  | ||||||
|         painter.fill_rect(cursor_rect, foreground_color()); |         painter.fill_rect(cursor_rect, foreground_color()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -76,6 +120,7 @@ void GTextBox::handle_backspace() | ||||||
|     if (m_text.length() == 1) { |     if (m_text.length() == 1) { | ||||||
|         m_text = String::empty(); |         m_text = String::empty(); | ||||||
|         m_cursor_position = 0; |         m_cursor_position = 0; | ||||||
|  |         m_scroll_offset = 0; | ||||||
|         if (on_change) |         if (on_change) | ||||||
|             on_change(*this); |             on_change(*this); | ||||||
|         update(); |         update(); | ||||||
|  | @ -90,6 +135,7 @@ void GTextBox::handle_backspace() | ||||||
| 
 | 
 | ||||||
|     m_text = move(new_text); |     m_text = move(new_text); | ||||||
|     --m_cursor_position; |     --m_cursor_position; | ||||||
|  |     scroll_cursor_into_view(HorizontalDirection::Left); | ||||||
|     if (on_change) |     if (on_change) | ||||||
|         on_change(*this); |         on_change(*this); | ||||||
|     update(); |     update(); | ||||||
|  | @ -99,14 +145,18 @@ void GTextBox::keydown_event(GKeyEvent& event) | ||||||
| { | { | ||||||
|     switch (event.key()) { |     switch (event.key()) { | ||||||
|     case KeyCode::Key_Left: |     case KeyCode::Key_Left: | ||||||
|         if (m_cursor_position) |         if (m_cursor_position) { | ||||||
|             --m_cursor_position; |             --m_cursor_position; | ||||||
|  |             scroll_cursor_into_view(HorizontalDirection::Left); | ||||||
|  |         } | ||||||
|         m_cursor_blink_state = true; |         m_cursor_blink_state = true; | ||||||
|         update(); |         update(); | ||||||
|         return; |         return; | ||||||
|     case KeyCode::Key_Right: |     case KeyCode::Key_Right: | ||||||
|         if (m_cursor_position < m_text.length()) |         if (m_cursor_position < m_text.length()) { | ||||||
|             ++m_cursor_position; |             ++m_cursor_position; | ||||||
|  |             scroll_cursor_into_view(HorizontalDirection::Right); | ||||||
|  |         } | ||||||
|         m_cursor_blink_state = true; |         m_cursor_blink_state = true; | ||||||
|         update(); |         update(); | ||||||
|         return; |         return; | ||||||
|  | @ -130,6 +180,7 @@ void GTextBox::keydown_event(GKeyEvent& event) | ||||||
| 
 | 
 | ||||||
|         m_text = move(new_text); |         m_text = move(new_text); | ||||||
|         ++m_cursor_position; |         ++m_cursor_position; | ||||||
|  |         scroll_cursor_into_view(HorizontalDirection::Right); | ||||||
|         if (on_change) |         if (on_change) | ||||||
|             on_change(*this); |             on_change(*this); | ||||||
|         update(); |         update(); | ||||||
|  | @ -156,3 +207,8 @@ void GTextBox::focusout_event(GEvent&) | ||||||
| { | { | ||||||
|     stop_timer(); |     stop_timer(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void GTextBox::resize_event(GResizeEvent&) | ||||||
|  | { | ||||||
|  |     scroll_cursor_into_view(HorizontalDirection::Right); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -22,12 +22,17 @@ private: | ||||||
|     virtual void timer_event(GTimerEvent&) override; |     virtual void timer_event(GTimerEvent&) override; | ||||||
|     virtual void focusin_event(GEvent&) override; |     virtual void focusin_event(GEvent&) override; | ||||||
|     virtual void focusout_event(GEvent&) override; |     virtual void focusout_event(GEvent&) override; | ||||||
|  |     virtual void resize_event(GResizeEvent&) override; | ||||||
|     virtual bool accepts_focus() const override { return true; } |     virtual bool accepts_focus() const override { return true; } | ||||||
| 
 | 
 | ||||||
|  |     Point cursor_content_position() const; | ||||||
|  |     Rect visible_content_rect() const; | ||||||
|     void handle_backspace(); |     void handle_backspace(); | ||||||
|  |     void scroll_cursor_into_view(HorizontalDirection); | ||||||
| 
 | 
 | ||||||
|     String m_text; |     String m_text; | ||||||
|     int m_cursor_position { 0 }; |     int m_cursor_position { 0 }; | ||||||
|  |     int m_scroll_offset { 0 }; | ||||||
|     bool m_cursor_blink_state { false }; |     bool m_cursor_blink_state { false }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,8 @@ class GWindow; | ||||||
| 
 | 
 | ||||||
| enum class SizePolicy { Fixed, Fill }; | enum class SizePolicy { Fixed, Fill }; | ||||||
| enum class Orientation { Horizontal, Vertical }; | enum class Orientation { Horizontal, Vertical }; | ||||||
|  | enum class HorizontalDirection { Left, Right }; | ||||||
|  | enum class VerticalDirection { Up, Down }; | ||||||
| 
 | 
 | ||||||
| class GWidget : public GObject { | class GWidget : public GObject { | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -9,9 +9,6 @@ | ||||||
| #include <LibC/errno.h> | #include <LibC/errno.h> | ||||||
| #include <LibC/mman.h> | #include <LibC/mman.h> | ||||||
| 
 | 
 | ||||||
| static Font* s_default_font; |  | ||||||
| static Font* s_default_bold_font; |  | ||||||
| 
 |  | ||||||
| struct [[gnu::packed]] FontFileHeader { | struct [[gnu::packed]] FontFileHeader { | ||||||
|     char magic[4]; |     char magic[4]; | ||||||
|     byte glyph_width; |     byte glyph_width; | ||||||
|  | @ -24,7 +21,8 @@ struct [[gnu::packed]] FontFileHeader { | ||||||
| 
 | 
 | ||||||
| Font& Font::default_font() | Font& Font::default_font() | ||||||
| { | { | ||||||
|     static const char* default_font_path = "/res/fonts/CsillaThin7x10.font"; |     static Font* s_default_font; | ||||||
|  |     static const char* default_font_path = "/res/fonts/Katica10.font"; | ||||||
|     if (!s_default_font) { |     if (!s_default_font) { | ||||||
|         s_default_font = Font::load_from_file(default_font_path).leak_ref(); |         s_default_font = Font::load_from_file(default_font_path).leak_ref(); | ||||||
|         ASSERT(s_default_font); |         ASSERT(s_default_font); | ||||||
|  | @ -32,8 +30,20 @@ Font& Font::default_font() | ||||||
|     return *s_default_font; |     return *s_default_font; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Font& Font::default_fixed_width_font() | ||||||
|  | { | ||||||
|  |     static Font* s_default_fixed_width_font; | ||||||
|  |     static const char* default_fixed_width_font_path = "/res/fonts/CsillaThin7x10.font"; | ||||||
|  |     if (!s_default_fixed_width_font) { | ||||||
|  |         s_default_fixed_width_font = Font::load_from_file(default_fixed_width_font_path).leak_ref(); | ||||||
|  |         ASSERT(s_default_fixed_width_font); | ||||||
|  |     } | ||||||
|  |     return *s_default_fixed_width_font; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Font& Font::default_bold_font() | Font& Font::default_bold_font() | ||||||
| { | { | ||||||
|  |     static Font* s_default_bold_font; | ||||||
|     static const char* default_bold_font_path = "/res/fonts/CsillaBold7x10.font"; |     static const char* default_bold_font_path = "/res/fonts/CsillaBold7x10.font"; | ||||||
|     if (!s_default_bold_font) { |     if (!s_default_bold_font) { | ||||||
|         s_default_bold_font = Font::load_from_file(default_bold_font_path).leak_ref(); |         s_default_bold_font = Font::load_from_file(default_bold_font_path).leak_ref(); | ||||||
|  |  | ||||||
|  | @ -43,6 +43,8 @@ public: | ||||||
|     static Font& default_font(); |     static Font& default_font(); | ||||||
|     static Font& default_bold_font(); |     static Font& default_bold_font(); | ||||||
| 
 | 
 | ||||||
|  |     static Font& default_fixed_width_font(); | ||||||
|  | 
 | ||||||
|     RetainPtr<Font> clone() const; |     RetainPtr<Font> clone() const; | ||||||
| 
 | 
 | ||||||
|     static RetainPtr<Font> load_from_memory(const byte*); |     static RetainPtr<Font> load_from_memory(const byte*); | ||||||
|  |  | ||||||
|  | @ -99,6 +99,7 @@ GWindow* make_launcher_window() | ||||||
| 
 | 
 | ||||||
|     auto* other_textbox = new GTextBox(widget); |     auto* other_textbox = new GTextBox(widget); | ||||||
|     other_textbox->set_relative_rect({ 5, 140, 90, 20 }); |     other_textbox->set_relative_rect({ 5, 140, 90, 20 }); | ||||||
|  |     other_textbox->set_text("Hello there I am text."); | ||||||
| 
 | 
 | ||||||
|     auto* checkbox = new GCheckBox(widget); |     auto* checkbox = new GCheckBox(widget); | ||||||
|     checkbox->set_relative_rect({ 5, 170, 90, 20 }); |     checkbox->set_relative_rect({ 5, 170, 90, 20 }); | ||||||
|  |  | ||||||
|  | @ -359,11 +359,13 @@ void WSWindowManager::set_current_menubar(WSMenuBar* menubar) | ||||||
|     dbgprintf("[WM] Current menubar is now %p\n", menubar); |     dbgprintf("[WM] Current menubar is now %p\n", menubar); | ||||||
| #endif | #endif | ||||||
|     Point next_menu_location { menubar_menu_margin() / 2, 0 }; |     Point next_menu_location { menubar_menu_margin() / 2, 0 }; | ||||||
|  |     int index = 0; | ||||||
|     for_each_active_menubar_menu([&] (WSMenu& menu) { |     for_each_active_menubar_menu([&] (WSMenu& menu) { | ||||||
|         int text_width = font().width(menu.name()); |         int text_width = index == 1 ? Font::default_bold_font().width(menu.name()) : font().width(menu.name()); | ||||||
|         menu.set_rect_in_menubar({ next_menu_location.x() - menubar_menu_margin() / 2, 0, text_width + menubar_menu_margin(), menubar_rect().height() - 1 }); |         menu.set_rect_in_menubar({ next_menu_location.x() - menubar_menu_margin() / 2, 0, text_width + menubar_menu_margin(), menubar_rect().height() - 1 }); | ||||||
|         menu.set_text_rect_in_menubar({ next_menu_location, { text_width, menubar_rect().height() } }); |         menu.set_text_rect_in_menubar({ next_menu_location, { text_width, menubar_rect().height() } }); | ||||||
|         next_menu_location.move_by(menu.rect_in_menubar().width(), 0); |         next_menu_location.move_by(menu.rect_in_menubar().width(), 0); | ||||||
|  |         ++index; | ||||||
|         return true; |         return true; | ||||||
|     }); |     }); | ||||||
|     invalidate(menubar_rect()); |     invalidate(menubar_rect()); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling