diff --git a/AK/DoublyLinkedList.h b/AK/DoublyLinkedList.h index 14661acf0ec..2aa804175b9 100644 --- a/AK/DoublyLinkedList.h +++ b/AK/DoublyLinkedList.h @@ -64,6 +64,7 @@ public: bool operator==(const Iterator& other) const { return m_node == other.m_node; } Iterator& operator++() { m_node = m_node->next; return *this; } T& operator*() { return m_node->value; } + T* operator->() { return &m_node->value; } bool is_end() const { return !m_node; } static Iterator universal_end() { return Iterator(nullptr); } private: @@ -81,6 +82,7 @@ public: bool operator==(const ConstIterator& other) const { return m_node == other.m_node; } ConstIterator& operator++() { m_node = m_node->next; return *this; } const T& operator*() const { return m_node->value; } + const T* operator->() const { return &m_node->value; } bool is_end() const { return !m_node; } static ConstIterator universal_end() { return ConstIterator(nullptr); } private: diff --git a/AK/HashTable.h b/AK/HashTable.h index 0418af7d666..0bb6bda7ef6 100644 --- a/AK/HashTable.h +++ b/AK/HashTable.h @@ -75,6 +75,7 @@ public: #endif return *m_bucket_iterator; } + T* operator->() { return m_bucket_iterator.operator->(); } Iterator& operator++() { skip_to_next(); @@ -151,6 +152,7 @@ public: #endif return *m_bucket_iterator; } + const T* operator->() const { return m_bucket_iterator.operator->(); } ConstIterator& operator++() { skip_to_next(); diff --git a/Applications/FileManager/DirectoryModel.cpp b/Applications/FileManager/DirectoryModel.cpp index b2996abb59d..16e0b3e4811 100644 --- a/Applications/FileManager/DirectoryModel.cpp +++ b/Applications/FileManager/DirectoryModel.cpp @@ -39,12 +39,12 @@ DirectoryModel::DirectoryModel() { create_thread(thumbnail_thread, this); - m_directory_icon = GraphicsBitmap::load_from_file("/res/icons/folder16.png"); - m_file_icon = GraphicsBitmap::load_from_file("/res/icons/file16.png"); - m_symlink_icon = GraphicsBitmap::load_from_file("/res/icons/link16.png"); - m_socket_icon = GraphicsBitmap::load_from_file("/res/icons/socket16.png"); - m_executable_icon = GraphicsBitmap::load_from_file("/res/icons/executable16.png"); - m_filetype_image_icon = GraphicsBitmap::load_from_file("/res/icons/16x16/filetype-image.png"); + m_directory_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/folder16.png"), GraphicsBitmap::load_from_file("/res/icons/32x32/folder.png")); + m_file_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/file16.png"), GraphicsBitmap::load_from_file("/res/icons/32x32/file.png")); + m_symlink_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/link16.png")); + m_socket_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/socket16.png")); + m_executable_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/executable16.png"), GraphicsBitmap::load_from_file("/res/icons/32x32/filetype-executable.png")); + m_filetype_image_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/16x16/filetype-image.png"), GraphicsBitmap::load_from_file("/res/icons/32x32/filetype-image.png")); setpwent(); while (auto* passwd = getpwent()) @@ -99,16 +99,16 @@ GModel::ColumnMetadata DirectoryModel::column_metadata(int column) const ASSERT_NOT_REACHED(); } -const GraphicsBitmap& DirectoryModel::icon_for(const Entry& entry) const +GIcon DirectoryModel::icon_for(const Entry& entry) const { if (S_ISDIR(entry.mode)) - return *m_directory_icon; + return m_directory_icon; if (S_ISLNK(entry.mode)) - return *m_symlink_icon; + return m_symlink_icon; if (S_ISSOCK(entry.mode)) - return *m_socket_icon; + return m_socket_icon; if (entry.mode & S_IXUSR) - return *m_executable_icon; + return m_executable_icon; if (entry.name.ends_with(".png")) { if (!entry.thumbnail) { auto path = entry.full_path(*this); @@ -120,10 +120,10 @@ const GraphicsBitmap& DirectoryModel::icon_for(const Entry& entry) const } } if (!entry.thumbnail) - return *m_filetype_image_icon; - return *entry.thumbnail; + return m_filetype_image_icon; + return GIcon(*entry.thumbnail); } - return *m_file_icon; + return m_file_icon; } static String permission_string(mode_t mode) diff --git a/Applications/FileManager/DirectoryModel.h b/Applications/FileManager/DirectoryModel.h index f78b0bfb14b..4196cd586a4 100644 --- a/Applications/FileManager/DirectoryModel.h +++ b/Applications/FileManager/DirectoryModel.h @@ -58,19 +58,19 @@ private: return m_directories[index]; return m_files[index - m_directories.size()]; } - const GraphicsBitmap& icon_for(const Entry& entry) const; + GIcon icon_for(const Entry& entry) const; String m_path; Vector m_files; Vector m_directories; size_t m_bytes_in_files; - RetainPtr m_directory_icon; - RetainPtr m_file_icon; - RetainPtr m_symlink_icon; - RetainPtr m_socket_icon; - RetainPtr m_executable_icon; - RetainPtr m_filetype_image_icon; + GIcon m_directory_icon; + GIcon m_file_icon; + GIcon m_symlink_icon; + GIcon m_socket_icon; + GIcon m_executable_icon; + GIcon m_filetype_image_icon; HashMap m_user_names; HashMap m_group_names; diff --git a/Base/res/icons/32x32/filetype-image.png b/Base/res/icons/32x32/filetype-image.png new file mode 100644 index 00000000000..2e219bf46bd Binary files /dev/null and b/Base/res/icons/32x32/filetype-image.png differ diff --git a/LibGUI/GIcon.cpp b/LibGUI/GIcon.cpp new file mode 100644 index 00000000000..16224f1a971 --- /dev/null +++ b/LibGUI/GIcon.cpp @@ -0,0 +1,63 @@ +#include + +GIcon::GIcon() + : m_impl(GIconImpl::create()) +{ +} + +GIcon::GIcon(const GIconImpl& impl) + : m_impl(const_cast(impl)) +{ +} + +GIcon::GIcon(const GIcon& other) + : m_impl(other.m_impl.copy_ref()) +{ +} + +GIcon::GIcon(RetainPtr&& bitmap) + : GIcon() +{ + if (bitmap) { + ASSERT(bitmap->width() == bitmap->height()); + int size = bitmap->width(); + set_bitmap_for_size(size, move(bitmap)); + } +} + +GIcon::GIcon(RetainPtr&& bitmap1, RetainPtr&& bitmap2) + : GIcon(move(bitmap1)) +{ + if (bitmap2) { + ASSERT(bitmap2->width() == bitmap2->height()); + int size = bitmap2->width(); + set_bitmap_for_size(size, move(bitmap2)); + } +} + +const GraphicsBitmap* GIconImpl::bitmap_for_size(int size) const +{ + auto it = m_bitmaps.find(size); + if (it != m_bitmaps.end()) + return it->value.ptr(); + + int best_diff_so_far = INT32_MAX; + const GraphicsBitmap* best_fit = nullptr; + for (auto& it : m_bitmaps) { + int abs_diff = abs(it.key - size); + if (abs_diff < best_diff_so_far) { + best_diff_so_far = abs_diff; + best_fit = it.value.ptr(); + } + } + return best_fit; +} + +void GIconImpl::set_bitmap_for_size(int size, RetainPtr&& bitmap) +{ + if (!bitmap) { + m_bitmaps.remove(size); + return; + } + m_bitmaps.set(size, move(bitmap)); +} diff --git a/LibGUI/GIcon.h b/LibGUI/GIcon.h new file mode 100644 index 00000000000..bfb48496bad --- /dev/null +++ b/LibGUI/GIcon.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +class GIconImpl : public Retainable { +public: + static Retained create() { return adopt(*new GIconImpl); } + ~GIconImpl() { } + + const GraphicsBitmap* bitmap_for_size(int) const; + void set_bitmap_for_size(int, RetainPtr&&); + +private: + GIconImpl() { } + HashMap> m_bitmaps; +}; + +class GIcon { +public: + GIcon(); + explicit GIcon(RetainPtr&&); + explicit GIcon(RetainPtr&&, RetainPtr&&); + explicit GIcon(const GIconImpl&); + GIcon(const GIcon&); + ~GIcon() { } + + GIcon& operator=(const GIcon& other) + { + m_impl = other.m_impl.copy_ref(); + return *this; + } + + const GraphicsBitmap* bitmap_for_size(int size) const { return m_impl->bitmap_for_size(size); } + void set_bitmap_for_size(int size, RetainPtr&& bitmap) { m_impl->set_bitmap_for_size(size, move(bitmap)); } + + const GIconImpl& impl() const { return *m_impl; } + +private: + Retained m_impl; +}; diff --git a/LibGUI/GItemView.cpp b/LibGUI/GItemView.cpp index db0499c1215..39c2f2cb04e 100644 --- a/LibGUI/GItemView.cpp +++ b/LibGUI/GItemView.cpp @@ -110,8 +110,9 @@ void GItemView::paint_event(GPaintEvent& event) icon_rect.center_within(item_rect); icon_rect.move_by(0, -font.glyph_height() - 6); - if (icon.is_bitmap()) { - painter.draw_scaled_bitmap(icon_rect, icon.as_bitmap(), icon.as_bitmap().rect()); + if (icon.is_icon()) { + if (auto bitmap = icon.as_icon().bitmap_for_size(icon_rect.width())) + painter.draw_scaled_bitmap(icon_rect, *bitmap, bitmap->rect()); } Rect text_rect { 0, icon_rect.bottom() + 6 + 1, font.width(item_text.to_string()), font.glyph_height() }; diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index e753002ab7b..81797be6263 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -141,6 +141,9 @@ void GTableView::paint_event(GPaintEvent& event) auto data = model()->data(cell_index); if (data.is_bitmap()) { painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect()); + } else if (data.is_icon()) { + if (auto bitmap = data.as_icon().bitmap_for_size(16)) + painter.blit(cell_rect.location(), *bitmap, bitmap->rect()); } else { Color text_color; if (is_selected_row) diff --git a/LibGUI/GVariant.cpp b/LibGUI/GVariant.cpp index 1e8b7d187af..6f9d62a620f 100644 --- a/LibGUI/GVariant.cpp +++ b/LibGUI/GVariant.cpp @@ -15,6 +15,10 @@ GVariant::~GVariant() if (m_value.as_bitmap) m_value.as_bitmap->release(); break; + case Type::Icon: + if (m_value.as_icon) + m_value.as_icon->release(); + break; default: break; } @@ -52,6 +56,13 @@ GVariant::GVariant(const GraphicsBitmap& value) AK::retain_if_not_null(m_value.as_bitmap); } +GVariant::GVariant(const GIcon& value) + : m_type(Type::Icon) +{ + m_value.as_icon = &const_cast(value.impl()); + AK::retain_if_not_null(m_value.as_icon); +} + GVariant::GVariant(Color color) : m_type(Type::Color) { @@ -73,6 +84,8 @@ bool GVariant::operator==(const GVariant& other) const return as_string() == other.as_string(); case Type::Bitmap: return m_value.as_bitmap == other.m_value.as_bitmap; + case Type::Icon: + return m_value.as_icon == other.m_value.as_icon; case Type::Color: return m_value.as_color == other.m_value.as_color; case Type::Invalid: @@ -97,6 +110,9 @@ bool GVariant::operator<(const GVariant& other) const case Type::Bitmap: // FIXME: Maybe compare bitmaps somehow differently? return m_value.as_bitmap < other.m_value.as_bitmap; + case Type::Icon: + // FIXME: Maybe compare icons somehow differently? + return m_value.as_icon < other.m_value.as_icon; case Type::Color: return m_value.as_color < other.m_value.as_color; case Type::Invalid: @@ -118,6 +134,8 @@ String GVariant::to_string() const return as_string(); case Type::Bitmap: return "[GraphicsBitmap]"; + case Type::Icon: + return "[GIcon]"; case Type::Color: return as_color().to_string(); case Type::Invalid: diff --git a/LibGUI/GVariant.h b/LibGUI/GVariant.h index 13461880159..3631c6e581e 100644 --- a/LibGUI/GVariant.h +++ b/LibGUI/GVariant.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include class GVariant { @@ -11,6 +12,7 @@ public: GVariant(int); GVariant(const String&); GVariant(const GraphicsBitmap&); + GVariant(const GIcon&); GVariant(Color); ~GVariant(); @@ -22,6 +24,7 @@ public: String, Bitmap, Color, + Icon, }; bool is_valid() const { return m_type != Type::Invalid; } @@ -31,6 +34,7 @@ public: bool is_string() const { return m_type == Type::String; } bool is_bitmap() const { return m_type == Type::Bitmap; } bool is_color() const { return m_type == Type::Color; } + bool is_icon() const { return m_type == Type::Icon; } Type type() const { return m_type; } bool as_bool() const @@ -63,6 +67,12 @@ public: return *m_value.as_bitmap; } + GIcon as_icon() const + { + ASSERT(type() == Type::Icon); + return GIcon(*m_value.as_icon); + } + Color as_color() const { ASSERT(type() == Type::Color); @@ -85,6 +95,7 @@ private: union { StringImpl* as_string; GraphicsBitmap* as_bitmap; + GIconImpl* as_icon; bool as_bool; int as_int; float as_float; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 1cd0beaf53a..6d67ef550fc 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -50,6 +50,7 @@ LIBGUI_OBJS = \ GProgressBar.o \ GAbstractView.o \ GItemView.o \ + GIcon.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)