Ver código fonte

LibGUI+FileManager: Add a GIcon class to support multi-size icons.

A GIcon can contain any number of bitmaps internally, and will give you
the best fitting icon when you call bitmap_for_size().
Andreas Kling 6 anos atrás
pai
commit
86413a6f5a

+ 2 - 0
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:

+ 2 - 0
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();

+ 14 - 14
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)

+ 7 - 7
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<Entry> m_files;
     Vector<Entry> m_directories;
     size_t m_bytes_in_files;
 
-    RetainPtr<GraphicsBitmap> m_directory_icon;
-    RetainPtr<GraphicsBitmap> m_file_icon;
-    RetainPtr<GraphicsBitmap> m_symlink_icon;
-    RetainPtr<GraphicsBitmap> m_socket_icon;
-    RetainPtr<GraphicsBitmap> m_executable_icon;
-    RetainPtr<GraphicsBitmap> 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<uid_t, String> m_user_names;
     HashMap<gid_t, String> m_group_names;

BIN
Base/res/icons/32x32/filetype-image.png


+ 63 - 0
LibGUI/GIcon.cpp

@@ -0,0 +1,63 @@
+#include <LibGUI/GIcon.h>
+
+GIcon::GIcon()
+    : m_impl(GIconImpl::create())
+{
+}
+
+GIcon::GIcon(const GIconImpl& impl)
+    : m_impl(const_cast<GIconImpl&>(impl))
+{
+}
+
+GIcon::GIcon(const GIcon& other)
+    : m_impl(other.m_impl.copy_ref())
+{
+}
+
+GIcon::GIcon(RetainPtr<GraphicsBitmap>&& bitmap)
+    : GIcon()
+{
+    if (bitmap) {
+        ASSERT(bitmap->width() == bitmap->height());
+        int size = bitmap->width();
+        set_bitmap_for_size(size, move(bitmap));
+    }
+}
+
+GIcon::GIcon(RetainPtr<GraphicsBitmap>&& bitmap1, RetainPtr<GraphicsBitmap>&& 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<GraphicsBitmap>&& bitmap)
+{
+    if (!bitmap) {
+        m_bitmaps.remove(size);
+        return;
+    }
+    m_bitmaps.set(size, move(bitmap));
+}

+ 41 - 0
LibGUI/GIcon.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include <SharedGraphics/GraphicsBitmap.h>
+#include <AK/HashMap.h>
+
+class GIconImpl : public Retainable<GIconImpl> {
+public:
+    static Retained<GIconImpl> create() { return adopt(*new GIconImpl); }
+    ~GIconImpl() { }
+
+    const GraphicsBitmap* bitmap_for_size(int) const;
+    void set_bitmap_for_size(int, RetainPtr<GraphicsBitmap>&&);
+
+private:
+    GIconImpl() { }
+    HashMap<int, RetainPtr<GraphicsBitmap>> m_bitmaps;
+};
+
+class GIcon {
+public:
+    GIcon();
+    explicit GIcon(RetainPtr<GraphicsBitmap>&&);
+    explicit GIcon(RetainPtr<GraphicsBitmap>&&, RetainPtr<GraphicsBitmap>&&);
+    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<GraphicsBitmap>&& bitmap) { m_impl->set_bitmap_for_size(size, move(bitmap)); }
+
+    const GIconImpl& impl() const { return *m_impl; }
+
+private:
+    Retained<GIconImpl> m_impl;
+};

+ 3 - 2
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() };

+ 3 - 0
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)

+ 18 - 0
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<GIconImpl&>(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:

+ 11 - 0
LibGUI/GVariant.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include <AK/AKString.h>
+#include <LibGUI/GIcon.h>
 #include <SharedGraphics/GraphicsBitmap.h>
 
 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;

+ 1 - 0
LibGUI/Makefile

@@ -50,6 +50,7 @@ LIBGUI_OBJS = \
     GProgressBar.o \
     GAbstractView.o \
     GItemView.o \
+    GIcon.o \
     GWindow.o
 
 OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)