perf: optimize startup by caching icons, texture, and async avatar
- Replace manual icon theme lookup + Pixbuf scaling with native GTK4 Image::from_icon_name() (uses internal cache + GPU rendering) - Decode wallpaper texture once and share across all windows instead of N+1 separate JPEG decodes - Load file-based avatars asynchronously via gio::spawn_blocking to avoid blocking the UI thread
This commit is contained in:
parent
d6979c1792
commit
b22172c3a0
@ -51,12 +51,13 @@ fn activate(app: >k::Application) {
|
||||
|
||||
load_css(&display);
|
||||
|
||||
// Resolve wallpaper once, share across all windows
|
||||
// Resolve wallpaper once, decode texture once, share across all windows
|
||||
let config = config::load_config(None);
|
||||
let bg_path = config::resolve_background_path(&config);
|
||||
let texture = panel::load_background_texture(&bg_path);
|
||||
|
||||
// Panel on focused output (no set_monitor → compositor picks focused)
|
||||
let panel = panel::create_panel_window(&bg_path, app);
|
||||
let panel = panel::create_panel_window(&texture, app);
|
||||
setup_layer_shell(&panel, true, gtk4_layer_shell::Layer::Overlay);
|
||||
panel.present();
|
||||
|
||||
@ -64,7 +65,7 @@ fn activate(app: >k::Application) {
|
||||
let monitors = display.monitors();
|
||||
for i in 0..monitors.n_items() {
|
||||
if let Some(monitor) = monitors.item(i).and_then(|obj| obj.downcast::<gdk::Monitor>().ok()) {
|
||||
let wallpaper = panel::create_wallpaper_window(&bg_path, app);
|
||||
let wallpaper = panel::create_wallpaper_window(&texture, app);
|
||||
setup_layer_shell(&wallpaper, false, gtk4_layer_shell::Layer::Top);
|
||||
wallpaper.set_monitor(Some(&monitor));
|
||||
wallpaper.present();
|
||||
|
||||
108
src/panel.rs
108
src/panel.rs
@ -79,14 +79,26 @@ pub fn action_definitions() -> Vec<ActionDef> {
|
||||
]
|
||||
}
|
||||
|
||||
/// Load the wallpaper as a texture once, for sharing across all windows.
|
||||
pub fn load_background_texture(bg_path: &Path) -> gdk::Texture {
|
||||
if bg_path.starts_with("/dev/moonarch/moonset") {
|
||||
gdk::Texture::from_resource(bg_path.to_str().unwrap_or(""))
|
||||
} else {
|
||||
let file = gio::File::for_path(bg_path);
|
||||
gdk::Texture::from_file(&file).unwrap_or_else(|_| {
|
||||
gdk::Texture::from_resource("/dev/moonarch/moonset/wallpaper.jpg")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a wallpaper-only window for secondary monitors.
|
||||
pub fn create_wallpaper_window(bg_path: &Path, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
pub fn create_wallpaper_window(texture: &gdk::Texture, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.build();
|
||||
window.add_css_class("wallpaper");
|
||||
|
||||
let background = create_background_picture(bg_path);
|
||||
let background = create_background_picture(texture);
|
||||
window.set_child(Some(&background));
|
||||
|
||||
// Fade-in on map
|
||||
@ -104,7 +116,7 @@ pub fn create_wallpaper_window(bg_path: &Path, app: >k::Application) -> gtk::A
|
||||
}
|
||||
|
||||
/// Create the main panel window with action buttons and confirm flow.
|
||||
pub fn create_panel_window(bg_path: &Path, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
pub fn create_panel_window(texture: &gdk::Texture, app: >k::Application) -> gtk::ApplicationWindow {
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.build();
|
||||
@ -126,7 +138,7 @@ pub fn create_panel_window(bg_path: &Path, app: >k::Application) -> gtk::Appli
|
||||
window.set_child(Some(&overlay));
|
||||
|
||||
// Background wallpaper
|
||||
let background = create_background_picture(bg_path);
|
||||
let background = create_background_picture(texture);
|
||||
overlay.set_child(Some(&background));
|
||||
|
||||
// Click on background dismisses the menu
|
||||
@ -157,13 +169,8 @@ pub fn create_panel_window(bg_path: &Path, app: >k::Application) -> gtk::Appli
|
||||
avatar_frame.append(&avatar_image);
|
||||
content_box.append(&avatar_frame);
|
||||
|
||||
// Load avatar
|
||||
let avatar_path = users::get_avatar_path(&user.home, Some(&user.username));
|
||||
if let Some(path) = avatar_path {
|
||||
set_avatar_from_file(&avatar_image, &path);
|
||||
} else {
|
||||
set_default_avatar(&avatar_image, &window);
|
||||
}
|
||||
// Load avatar (file-based avatars load asynchronously)
|
||||
load_avatar_async(&avatar_image, &window, &user);
|
||||
|
||||
// Username label
|
||||
let username_label = gtk::Label::new(Some(&user.display_name));
|
||||
@ -237,13 +244,9 @@ pub fn create_panel_window(bg_path: &Path, app: >k::Application) -> gtk::Appli
|
||||
window
|
||||
}
|
||||
|
||||
/// Create a Picture widget for the wallpaper background.
|
||||
fn create_background_picture(bg_path: &Path) -> gtk::Picture {
|
||||
let background = if bg_path.starts_with("/dev/moonarch/moonset") {
|
||||
gtk::Picture::for_resource(bg_path.to_str().unwrap_or(""))
|
||||
} else {
|
||||
gtk::Picture::for_filename(bg_path.to_str().unwrap_or(""))
|
||||
};
|
||||
/// Create a Picture widget for the wallpaper background from a shared texture.
|
||||
fn create_background_picture(texture: &gdk::Texture) -> gtk::Picture {
|
||||
let background = gtk::Picture::for_paintable(texture);
|
||||
background.set_content_fit(gtk::ContentFit::Cover);
|
||||
background.set_hexpand(true);
|
||||
background.set_vexpand(true);
|
||||
@ -302,34 +305,9 @@ fn create_action_button(
|
||||
button
|
||||
}
|
||||
|
||||
/// Load a symbolic icon at 22px and scale to 64px via GdkPixbuf.
|
||||
/// Load a symbolic icon using native GTK4 rendering at the target size.
|
||||
fn load_scaled_icon(icon_name: &str) -> gtk::Image {
|
||||
let display = gdk::Display::default().unwrap();
|
||||
let theme = gtk::IconTheme::for_display(&display);
|
||||
let icon_paintable = theme.lookup_icon(
|
||||
icon_name,
|
||||
&[],
|
||||
22,
|
||||
1,
|
||||
gtk::TextDirection::None,
|
||||
gtk::IconLookupFlags::FORCE_SYMBOLIC,
|
||||
);
|
||||
|
||||
let icon = gtk::Image::new();
|
||||
if let Some(file) = icon_paintable.file() {
|
||||
if let Some(path) = file.path() {
|
||||
if let Ok(pixbuf) =
|
||||
Pixbuf::from_file_at_scale(path.to_str().unwrap_or(""), 64, 64, true)
|
||||
{
|
||||
let texture = gdk::Texture::for_pixbuf(&pixbuf);
|
||||
icon.set_paintable(Some(&texture));
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: use icon name directly
|
||||
icon.set_icon_name(Some(icon_name));
|
||||
let icon = gtk::Image::from_icon_name(icon_name);
|
||||
icon.set_pixel_size(64);
|
||||
icon
|
||||
}
|
||||
@ -479,15 +457,39 @@ fn execute_action(
|
||||
));
|
||||
}
|
||||
|
||||
/// Load an image file and set it as the avatar.
|
||||
fn set_avatar_from_file(image: >k::Image, path: &Path) {
|
||||
match Pixbuf::from_file_at_scale(path.to_str().unwrap_or(""), AVATAR_SIZE, AVATAR_SIZE, true) {
|
||||
Ok(pixbuf) => {
|
||||
let texture = gdk::Texture::for_pixbuf(&pixbuf);
|
||||
image.set_paintable(Some(&texture));
|
||||
/// Load the avatar asynchronously. File-based avatars are decoded off the UI thread.
|
||||
fn load_avatar_async(image: >k::Image, window: >k::ApplicationWindow, user: &users::User) {
|
||||
let avatar_path = users::get_avatar_path(&user.home, Some(&user.username));
|
||||
|
||||
match avatar_path {
|
||||
Some(path) => {
|
||||
// File-based avatar: load and scale in background thread
|
||||
glib::spawn_future_local(clone!(
|
||||
#[weak]
|
||||
image,
|
||||
async move {
|
||||
let result = gio::spawn_blocking(move || {
|
||||
Pixbuf::from_file_at_scale(
|
||||
path.to_str().unwrap_or(""),
|
||||
AVATAR_SIZE,
|
||||
AVATAR_SIZE,
|
||||
true,
|
||||
)
|
||||
.ok()
|
||||
.map(|pb| gdk::Texture::for_pixbuf(&pb))
|
||||
})
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(Some(texture)) => image.set_paintable(Some(&texture)),
|
||||
_ => image.set_icon_name(Some("avatar-default-symbolic")),
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
Err(_) => {
|
||||
image.set_icon_name(Some("avatar-default-symbolic"));
|
||||
None => {
|
||||
// Default SVG avatar: needs widget color, keep synchronous
|
||||
set_default_avatar(image, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user