Skip to content

Commit c44dbab

Browse files
committed
LibGUI: Generate nicer icons for symlinked files
Instead of symlinks showing up with the "filetype-symlink" icon, we now generate a new icon by taking the target file's icon and slapping a small arrow emblem on top of it. This looks rather nice. :^)
1 parent 3d5e30a commit c44dbab

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed
114 Bytes
Loading

Base/res/icons/symlink-emblem.png

122 Bytes
Loading

Libraries/LibGUI/FileIconProvider.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2525
*/
2626

27+
#include <AK/LexicalPath.h>
2728
#include <AK/String.h>
2829
#include <LibCore/ConfigFile.h>
30+
#include <LibCore/File.h>
2931
#include <LibCore/StandardPaths.h>
3032
#include <LibGUI/FileIconProvider.h>
3133
#include <LibGUI/Icon.h>
34+
#include <LibGUI/Painter.h>
3235
#include <LibGfx/Bitmap.h>
3336
#include <sys/stat.h>
3437

@@ -45,6 +48,8 @@ static Icon s_symlink_icon;
4548
static Icon s_socket_icon;
4649
static Icon s_executable_icon;
4750
static Icon s_filetype_image_icon;
51+
static RefPtr<Gfx::Bitmap> s_symlink_emblem;
52+
static RefPtr<Gfx::Bitmap> s_symlink_emblem_small;
4853

4954
static HashMap<String, Icon> s_filetype_icons;
5055
static HashMap<String, Vector<String>> s_filetype_patterns;
@@ -57,6 +62,9 @@ static void initialize_if_needed()
5762

5863
auto config = Core::ConfigFile::open("/etc/FileIconProvider.ini");
5964

65+
s_symlink_emblem = Gfx::Bitmap::load_from_file("/res/icons/symlink-emblem.png");
66+
s_symlink_emblem_small = Gfx::Bitmap::load_from_file("/res/icons/symlink-emblem-small.png");
67+
6068
s_hard_disk_icon = Icon::default_icon("hard-disk");
6169
s_directory_icon = Icon::default_icon("filetype-folder");
6270
s_directory_open_icon = Icon::default_icon("filetype-folder-open");
@@ -127,8 +135,36 @@ Icon FileIconProvider::icon_for_path(const String& path, mode_t mode)
127135
return s_inaccessible_directory_icon;
128136
return s_directory_icon;
129137
}
130-
if (S_ISLNK(mode))
131-
return s_symlink_icon;
138+
if (S_ISLNK(mode)) {
139+
auto raw_symlink_target = Core::File::read_link(path);
140+
if (raw_symlink_target.is_null())
141+
return s_symlink_icon;
142+
143+
String target_path;
144+
if (raw_symlink_target.starts_with('/')) {
145+
target_path = raw_symlink_target;
146+
} else {
147+
target_path = Core::File::real_path_for(String::formatted("{}/{}", LexicalPath(path).dirname(), raw_symlink_target));
148+
}
149+
auto target_icon = icon_for_path(target_path);
150+
151+
Icon generated_icon;
152+
for (auto size : target_icon.sizes()) {
153+
auto& emblem = size < 32 ? *s_symlink_emblem_small : *s_symlink_emblem;
154+
auto original_bitmap = target_icon.bitmap_for_size(size);
155+
ASSERT(original_bitmap);
156+
auto generated_bitmap = original_bitmap->clone();
157+
if (!generated_bitmap) {
158+
dbgln("Failed to clone {}x{} icon for symlink variant", size, size);
159+
return s_symlink_icon;
160+
}
161+
GUI::Painter painter(*generated_bitmap);
162+
painter.blit({ size - emblem.width(), size - emblem.height() }, emblem, emblem.rect());
163+
164+
generated_icon.set_bitmap_for_size(size, move(generated_bitmap));
165+
}
166+
return generated_icon;
167+
}
132168
if (S_ISSOCK(mode))
133169
return s_socket_icon;
134170
if (mode & (S_IXUSR | S_IXGRP | S_IXOTH))

0 commit comments

Comments
 (0)