Aylur / astal

Building blocks for creating custom desktop shells
https://aylur.github.io/astal/
GNU Lesser General Public License v2.1
279 stars 35 forks source link

hyprland focused client icons flickering/takes too long to load #116

Closed danielwerg closed 1 day ago

danielwerg commented 5 days ago

Have you read through the documentation?

Describe what have you tried so far

Looking up icon for focused client:

const icon = Gtk.IconTheme
  .get_default()
  .lookup_icon('vscodium', 32, Gtk.IconLookupFlags.USE_BUILTIN)
  ?.get_filename();

And render:

icon && <icon icon={icon} />

I already have applied min-width and min-height to <icon>, adding transition: all 1s does nothing.

Describe your question

On re-render focused client icons noticeably "flickering", especially bad with non-svg icons (ex: vscodium). Is there a recommended way to deal with it, like load icons fast or/and make transition smoother?

Here is example of me switching between 2 different windows (not tabs):

https://github.com/user-attachments/assets/a05ea83d-5dd9-4a5a-8559-2ec2ca2ea655

https://github.com/user-attachments/assets/ac08dc21-eb6a-4f09-b726-22ac75ef75dc

I have also compared with waybar, there no flickering and vscodium icon loads faster.

calebstewart commented 1 day ago

I'm pretty new to Astal/AGS, but I stumbled on your issue while tinkering, and wanted to share my experience. The Astal.Icon widget doesn't need a file path. It can take the icon name directly, and this is working great for me with no flickering or delayed rendering. My best guess is that by manually resolving the icon path yourself you are accidentally bypassing some internal caching either by GTK or Astal. My active client component looks like this:

function ActiveClient(monitor: Hyprland.Monitor, monitorIndex: number) {
  var title = Variable("")
  var iconName = Variable("")

  // Connect to Hyprland events, so we can keep track of the
  // active client on our monitor, and set the appropriate
  // icon.
  var id = hyprland.connect("event", () => {
    var client = hyprland.focused_client
    if (client === null && hyprland.focused_monitor === monitor) {
      title.set(`Monitor ${monitorIndex}`)
    } else if (client !== null && client.monitor === monitor) {
      iconName.set(client.initial_class)
      title.set(client.title)
    }
  })

  return <revealer
    reveal_child={bind(title).as((t) => t != "")}
    transition_type={Gtk.RevealerTransitionType.SLIDE_LEFT} >
    <box className="ActiveClient">
      <icon icon={bind(iconName)} />
      <label
        onDestroy={(_) => {
          title.drop()
          hyprland.disconnect(id)
        }}
        label={bind(title)} />
    </box>
  </revealer>
}

I have no clue if this is an objectively "good" implementation, but it seems to be working for me, and I thought it might be helpful to you. :)

Aylur commented 1 day ago

File reading happens asynchronously which is why it flickers. Don't use filepaths when using named icons

function lookup(iconName) {
  return Gtk.IconTheme
    .get_default()
    .lookup_icon(iconName, 32, Gtk.IconLookupFlags.USE_BUILTIN)
}

{lookup('vscodium') && <icon icon="vscodium" />}
danielwerg commented 19 hours ago

@Aylur Thanks!

On a separate note, is there a proper way to lookup icon for hyprland client?

Right now I need to have a separate function to retrieve icon name from client class, for example:

codium -> vscodium
LibreWolf -> librewolf
...