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().
This commit is contained in:
Andreas Kling 2019-03-24 04:28:36 +01:00
parent 7e54fdce99
commit 86413a6f5a
Notes: sideshowbarker 2024-07-19 14:57:23 +09:00
12 changed files with 165 additions and 23 deletions

View file

@ -64,6 +64,7 @@ public:
bool operator==(const Iterator& other) const { return m_node == other.m_node; } bool operator==(const Iterator& other) const { return m_node == other.m_node; }
Iterator& operator++() { m_node = m_node->next; return *this; } Iterator& operator++() { m_node = m_node->next; return *this; }
T& operator*() { return m_node->value; } T& operator*() { return m_node->value; }
T* operator->() { return &m_node->value; }
bool is_end() const { return !m_node; } bool is_end() const { return !m_node; }
static Iterator universal_end() { return Iterator(nullptr); } static Iterator universal_end() { return Iterator(nullptr); }
private: private:
@ -81,6 +82,7 @@ public:
bool operator==(const ConstIterator& other) const { return m_node == other.m_node; } bool operator==(const ConstIterator& other) const { return m_node == other.m_node; }
ConstIterator& operator++() { m_node = m_node->next; return *this; } 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; }
const T* operator->() const { return &m_node->value; }
bool is_end() const { return !m_node; } bool is_end() const { return !m_node; }
static ConstIterator universal_end() { return ConstIterator(nullptr); } static ConstIterator universal_end() { return ConstIterator(nullptr); }
private: private:

View file

@ -75,6 +75,7 @@ public:
#endif #endif
return *m_bucket_iterator; return *m_bucket_iterator;
} }
T* operator->() { return m_bucket_iterator.operator->(); }
Iterator& operator++() Iterator& operator++()
{ {
skip_to_next(); skip_to_next();
@ -151,6 +152,7 @@ public:
#endif #endif
return *m_bucket_iterator; return *m_bucket_iterator;
} }
const T* operator->() const { return m_bucket_iterator.operator->(); }
ConstIterator& operator++() ConstIterator& operator++()
{ {
skip_to_next(); skip_to_next();

View file

@ -39,12 +39,12 @@ DirectoryModel::DirectoryModel()
{ {
create_thread(thumbnail_thread, this); create_thread(thumbnail_thread, this);
m_directory_icon = GraphicsBitmap::load_from_file("/res/icons/folder16.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 = GraphicsBitmap::load_from_file("/res/icons/file16.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 = GraphicsBitmap::load_from_file("/res/icons/link16.png"); m_symlink_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/link16.png"));
m_socket_icon = GraphicsBitmap::load_from_file("/res/icons/socket16.png"); m_socket_icon = GIcon(GraphicsBitmap::load_from_file("/res/icons/socket16.png"));
m_executable_icon = GraphicsBitmap::load_from_file("/res/icons/executable16.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 = GraphicsBitmap::load_from_file("/res/icons/16x16/filetype-image.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(); setpwent();
while (auto* passwd = getpwent()) while (auto* passwd = getpwent())
@ -99,16 +99,16 @@ GModel::ColumnMetadata DirectoryModel::column_metadata(int column) const
ASSERT_NOT_REACHED(); 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)) if (S_ISDIR(entry.mode))
return *m_directory_icon; return m_directory_icon;
if (S_ISLNK(entry.mode)) if (S_ISLNK(entry.mode))
return *m_symlink_icon; return m_symlink_icon;
if (S_ISSOCK(entry.mode)) if (S_ISSOCK(entry.mode))
return *m_socket_icon; return m_socket_icon;
if (entry.mode & S_IXUSR) if (entry.mode & S_IXUSR)
return *m_executable_icon; return m_executable_icon;
if (entry.name.ends_with(".png")) { if (entry.name.ends_with(".png")) {
if (!entry.thumbnail) { if (!entry.thumbnail) {
auto path = entry.full_path(*this); auto path = entry.full_path(*this);
@ -120,10 +120,10 @@ const GraphicsBitmap& DirectoryModel::icon_for(const Entry& entry) const
} }
} }
if (!entry.thumbnail) if (!entry.thumbnail)
return *m_filetype_image_icon; return m_filetype_image_icon;
return *entry.thumbnail; return GIcon(*entry.thumbnail);
} }
return *m_file_icon; return m_file_icon;
} }
static String permission_string(mode_t mode) static String permission_string(mode_t mode)

View file

@ -58,19 +58,19 @@ private:
return m_directories[index]; return m_directories[index];
return m_files[index - m_directories.size()]; 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; String m_path;
Vector<Entry> m_files; Vector<Entry> m_files;
Vector<Entry> m_directories; Vector<Entry> m_directories;
size_t m_bytes_in_files; size_t m_bytes_in_files;
RetainPtr<GraphicsBitmap> m_directory_icon; GIcon m_directory_icon;
RetainPtr<GraphicsBitmap> m_file_icon; GIcon m_file_icon;
RetainPtr<GraphicsBitmap> m_symlink_icon; GIcon m_symlink_icon;
RetainPtr<GraphicsBitmap> m_socket_icon; GIcon m_socket_icon;
RetainPtr<GraphicsBitmap> m_executable_icon; GIcon m_executable_icon;
RetainPtr<GraphicsBitmap> m_filetype_image_icon; GIcon m_filetype_image_icon;
HashMap<uid_t, String> m_user_names; HashMap<uid_t, String> m_user_names;
HashMap<gid_t, String> m_group_names; HashMap<gid_t, String> m_group_names;

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

63
LibGUI/GIcon.cpp Normal file
View file

@ -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
LibGUI/GIcon.h Normal file
View file

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

View file

@ -110,8 +110,9 @@ void GItemView::paint_event(GPaintEvent& event)
icon_rect.center_within(item_rect); icon_rect.center_within(item_rect);
icon_rect.move_by(0, -font.glyph_height() - 6); icon_rect.move_by(0, -font.glyph_height() - 6);
if (icon.is_bitmap()) { if (icon.is_icon()) {
painter.draw_scaled_bitmap(icon_rect, icon.as_bitmap(), icon.as_bitmap().rect()); 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() }; Rect text_rect { 0, icon_rect.bottom() + 6 + 1, font.width(item_text.to_string()), font.glyph_height() };

View file

@ -141,6 +141,9 @@ void GTableView::paint_event(GPaintEvent& event)
auto data = model()->data(cell_index); auto data = model()->data(cell_index);
if (data.is_bitmap()) { if (data.is_bitmap()) {
painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect()); 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 { } else {
Color text_color; Color text_color;
if (is_selected_row) if (is_selected_row)

View file

@ -15,6 +15,10 @@ GVariant::~GVariant()
if (m_value.as_bitmap) if (m_value.as_bitmap)
m_value.as_bitmap->release(); m_value.as_bitmap->release();
break; break;
case Type::Icon:
if (m_value.as_icon)
m_value.as_icon->release();
break;
default: default:
break; break;
} }
@ -52,6 +56,13 @@ GVariant::GVariant(const GraphicsBitmap& value)
AK::retain_if_not_null(m_value.as_bitmap); 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) GVariant::GVariant(Color color)
: m_type(Type::Color) : m_type(Type::Color)
{ {
@ -73,6 +84,8 @@ bool GVariant::operator==(const GVariant& other) const
return as_string() == other.as_string(); return as_string() == other.as_string();
case Type::Bitmap: case Type::Bitmap:
return m_value.as_bitmap == other.m_value.as_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: case Type::Color:
return m_value.as_color == other.m_value.as_color; return m_value.as_color == other.m_value.as_color;
case Type::Invalid: case Type::Invalid:
@ -97,6 +110,9 @@ bool GVariant::operator<(const GVariant& other) const
case Type::Bitmap: case Type::Bitmap:
// FIXME: Maybe compare bitmaps somehow differently? // FIXME: Maybe compare bitmaps somehow differently?
return m_value.as_bitmap < other.m_value.as_bitmap; 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: case Type::Color:
return m_value.as_color < other.m_value.as_color; return m_value.as_color < other.m_value.as_color;
case Type::Invalid: case Type::Invalid:
@ -118,6 +134,8 @@ String GVariant::to_string() const
return as_string(); return as_string();
case Type::Bitmap: case Type::Bitmap:
return "[GraphicsBitmap]"; return "[GraphicsBitmap]";
case Type::Icon:
return "[GIcon]";
case Type::Color: case Type::Color:
return as_color().to_string(); return as_color().to_string();
case Type::Invalid: case Type::Invalid:

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <AK/AKString.h> #include <AK/AKString.h>
#include <LibGUI/GIcon.h>
#include <SharedGraphics/GraphicsBitmap.h> #include <SharedGraphics/GraphicsBitmap.h>
class GVariant { class GVariant {
@ -11,6 +12,7 @@ public:
GVariant(int); GVariant(int);
GVariant(const String&); GVariant(const String&);
GVariant(const GraphicsBitmap&); GVariant(const GraphicsBitmap&);
GVariant(const GIcon&);
GVariant(Color); GVariant(Color);
~GVariant(); ~GVariant();
@ -22,6 +24,7 @@ public:
String, String,
Bitmap, Bitmap,
Color, Color,
Icon,
}; };
bool is_valid() const { return m_type != Type::Invalid; } 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_string() const { return m_type == Type::String; }
bool is_bitmap() const { return m_type == Type::Bitmap; } bool is_bitmap() const { return m_type == Type::Bitmap; }
bool is_color() const { return m_type == Type::Color; } bool is_color() const { return m_type == Type::Color; }
bool is_icon() const { return m_type == Type::Icon; }
Type type() const { return m_type; } Type type() const { return m_type; }
bool as_bool() const bool as_bool() const
@ -63,6 +67,12 @@ public:
return *m_value.as_bitmap; return *m_value.as_bitmap;
} }
GIcon as_icon() const
{
ASSERT(type() == Type::Icon);
return GIcon(*m_value.as_icon);
}
Color as_color() const Color as_color() const
{ {
ASSERT(type() == Type::Color); ASSERT(type() == Type::Color);
@ -85,6 +95,7 @@ private:
union { union {
StringImpl* as_string; StringImpl* as_string;
GraphicsBitmap* as_bitmap; GraphicsBitmap* as_bitmap;
GIconImpl* as_icon;
bool as_bool; bool as_bool;
int as_int; int as_int;
float as_float; float as_float;

View file

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