launcher: allow editing instance icon from home and preserve picture in dialog (fixes #8689)

Clicking the instance icon on Home or selecting 'Edit instance' in the
folder group more menu now opens the instance dialog. In the dialog,
clicking the large preview icon opens the image picker, and existing
pictures remain selectable in the palette.

Managed by Axxio.
Co-Authored-By: Antigravity <[email protected]>
This commit is contained in:
Axxio committed 2026-11-04 20:23:35 +01:00
1 parent 581fad0d32
commit ebaf4e7d21
3 files changed
+87 -19

No files matched your search

+31 -4
View File
@@ -68,7 +68,7 @@ impl Launcher {
.min_w_0()
.pt(px(64. - crate::glass::CONTENT_INSET))
.child(self.edition_tabs(java.is_some(), cx))
.child(self.home_title())
.child(self.home_title(cx))
.child(self.played_text(cx))
.child(actions)
}
@@ -143,18 +143,29 @@ impl Launcher {
}
/// The instance's icon and its name in Bricolage, 64 px at this width
/// (`clamp(2.75rem, 5vw, 4rem)`).
fn home_title(&mut self) -> Div {
/// (`clamp(2.75rem, 5vw, 4rem)`). Clickable to open the edit dialog (fixes #8689).
fn home_title(&mut self, cx: &mut Context<Self>) -> Div {
let Some(instance) = self.selected_instance().cloned() else {
return div();
};
let edit = instance.clone();
div()
.mt(px(20.))
.flex()
.items_center()
.gap(px(16.))
.min_w_0()
.child(self.glass_instance_icon(&instance, 56.))
.child(
div()
.id("home-instance-icon")
.cursor_pointer()
.hover(|s| s.opacity(0.85))
.on_click(cx.listener(move |this, _: &ClickEvent, _, cx| {
this.open_instance_dialog(Some(edit.clone()));
cx.notify();
}))
.child(self.glass_instance_icon(&instance, 56.)),
)
.child(
div()
.min_w_0()
@@ -752,6 +763,22 @@ impl Launcher {
}),
));
}
let edit = instance.clone();
let note = trf("Edit {name}", &[("name", &instance.name)]);
menu = menu.child(
ui::menu_item(
"folder-edit-instance",
tr("Edit instance").into(),
note,
false,
&t,
)
.on_click(cx.listener(move |this, _: &ClickEvent, _, cx| {
this.home_menu = HomeMenu::None;
this.open_instance_dialog(Some(edit.clone()));
cx.notify();
})),
);
wrap = wrap.child(
deferred(div().absolute().top(px(TALL + 8.)).left_0().child(menu)).with_priority(1),
);
+55 -15
View File
@@ -502,7 +502,17 @@ impl Launcher {
"New instance"
})),
)
.child(div().mt(px(24.)).child(icon))
.child(
div()
.id("aside-instance-icon")
.mt(px(24.))
.cursor_pointer()
.hover(|s| s.opacity(0.85))
.on_click(cx.listener(|this, _: &ClickEvent, _, cx| {
this.choose_instance_picture(cx);
}))
.child(icon),
)
.child(
div()
.mt(px(20.))
@@ -904,14 +914,16 @@ impl Launcher {
cx: &mut Context<Self>,
) -> Div {
let t = self.theme;
let Some(d) = self.instances.dialog.as_ref() else {
return div();
let (icon, logo, d_picture) = {
let Some(d) = self.instances.dialog.as_ref() else {
return div();
};
(d.icon.clone(), d.logo, d.picture.clone())
};
let (icon, logo) = (d.icon.clone(), d.logo);
let file_picture = matches!(d.picture, Some(Some(_))) && logo.is_none();
let own_picture = d.picture.is_none()
&& editing.is_some_and(|i| i.home.join(instances::PICTURE).is_file());
let pictured = matches!(d.picture, Some(Some(_))) || own_picture;
let has_saved_picture = editing.is_some_and(|i| i.home.join(instances::PICTURE).is_file());
let file_picture = matches!(&d_picture, Some(Some(_))) && logo.is_none();
let own_picture = d_picture.is_none() && has_saved_picture;
let pictured = matches!(&d_picture, Some(Some(_))) || own_picture;
let typed = self.servers.value(Field::InstanceName).trim().to_string();
let name = if typed.is_empty() { "N".into() } else { typed };
let pick = |icon: Option<Icon>| {
@@ -927,13 +939,41 @@ impl Launcher {
)
};
let mut first: Vec<AnyElement> = Vec::new();
if file_picture || own_picture {
let image = self.draft_icon(editing, kind, &name, 40.);
first.push(
icon_choice("icon-picture", true, &t)
.child(image)
.into_any_element(),
);
if file_picture || has_saved_picture {
let picture_img = match &d_picture {
Some(Some(chosen)) => {
use std::hash::{Hash, Hasher};
let mut h = std::collections::hash_map::DefaultHasher::new();
chosen.png.hash(&mut h);
self.icon_image(format!("chosen:{:x}", h.finish()), 40., false, || {
Some(chosen.png.clone())
})
}
_ => editing.and_then(|i| self.instance_picture(i, 40.)),
};
let picture_active = (file_picture || own_picture) && icon.is_none();
if let Some(image) = picture_img {
first.push(
icon_choice("icon-picture", picture_active, &t)
.child(instance_icons::centred(image, 40., self.scale))
.on_click(cx.listener(
move |this: &mut Self, _: &ClickEvent, _, cx: &mut Context<Self>| {
if picture_active {
this.choose_instance_picture(cx);
} else if let Some(d) = &mut this.instances.dialog {
d.icon = None;
d.logo = None;
if has_saved_picture {
d.picture = None;
}
d.error = None;
}
cx.notify();
},
))
.into_any_element(),
);
}
}
let loader = self.draft_loader();
let auto = self.auto_mark(kind, loader, 40.);
+1
View File
@@ -151,6 +151,7 @@ pub(super) fn centred(image: Arc<RenderImage>, size: f32, scale: f32) -> Div {
}
/// A picture chosen in the instance dialog, written on Save.
#[derive(Clone)]
pub(super) struct Chosen {
pub(super) png: Vec<u8>,
pub(super) preview: Arc<RenderImage>,