Przeglądaj źródła

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. :^)
Andreas Kling 4 lat temu
rodzic
commit
c44dbabda1

BIN
Base/res/icons/symlink-emblem-small.png


BIN
Base/res/icons/symlink-emblem.png


+ 38 - 2
Libraries/LibGUI/FileIconProvider.cpp

@@ -24,11 +24,14 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <AK/LexicalPath.h>
 #include <AK/String.h>
 #include <LibCore/ConfigFile.h>
+#include <LibCore/File.h>
 #include <LibCore/StandardPaths.h>
 #include <LibGUI/FileIconProvider.h>
 #include <LibGUI/Icon.h>
+#include <LibGUI/Painter.h>
 #include <LibGfx/Bitmap.h>
 #include <sys/stat.h>
 
@@ -45,6 +48,8 @@ static Icon s_symlink_icon;
 static Icon s_socket_icon;
 static Icon s_executable_icon;
 static Icon s_filetype_image_icon;
+static RefPtr<Gfx::Bitmap> s_symlink_emblem;
+static RefPtr<Gfx::Bitmap> s_symlink_emblem_small;
 
 static HashMap<String, Icon> s_filetype_icons;
 static HashMap<String, Vector<String>> s_filetype_patterns;
@@ -57,6 +62,9 @@ static void initialize_if_needed()
 
     auto config = Core::ConfigFile::open("/etc/FileIconProvider.ini");
 
+    s_symlink_emblem = Gfx::Bitmap::load_from_file("/res/icons/symlink-emblem.png");
+    s_symlink_emblem_small = Gfx::Bitmap::load_from_file("/res/icons/symlink-emblem-small.png");
+
     s_hard_disk_icon = Icon::default_icon("hard-disk");
     s_directory_icon = Icon::default_icon("filetype-folder");
     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)
             return s_inaccessible_directory_icon;
         return s_directory_icon;
     }
-    if (S_ISLNK(mode))
-        return s_symlink_icon;
+    if (S_ISLNK(mode)) {
+        auto raw_symlink_target = Core::File::read_link(path);
+        if (raw_symlink_target.is_null())
+            return s_symlink_icon;
+
+        String target_path;
+        if (raw_symlink_target.starts_with('/')) {
+            target_path = raw_symlink_target;
+        } else {
+            target_path = Core::File::real_path_for(String::formatted("{}/{}", LexicalPath(path).dirname(), raw_symlink_target));
+        }
+        auto target_icon = icon_for_path(target_path);
+
+        Icon generated_icon;
+        for (auto size : target_icon.sizes()) {
+            auto& emblem = size < 32 ? *s_symlink_emblem_small : *s_symlink_emblem;
+            auto original_bitmap = target_icon.bitmap_for_size(size);
+            ASSERT(original_bitmap);
+            auto generated_bitmap = original_bitmap->clone();
+            if (!generated_bitmap) {
+                dbgln("Failed to clone {}x{} icon for symlink variant", size, size);
+                return s_symlink_icon;
+            }
+            GUI::Painter painter(*generated_bitmap);
+            painter.blit({ size - emblem.width(), size - emblem.height() }, emblem, emblem.rect());
+
+            generated_icon.set_bitmap_for_size(size, move(generated_bitmap));
+        }
+        return generated_icon;
+    }
     if (S_ISSOCK(mode))
         return s_socket_icon;
     if (mode & (S_IXUSR | S_IXGRP | S_IXOTH))