UI: centre the Home New / Open buttons and links too (#4684)

The first commit centred the headings but left the New / Open buttons
and the community links on the left, as `horizontal` fills the width.
The buttons' row is now padded by its measured width, and the Home
screen's links are centred (label only, no icon; About keeps its
left-aligned icon rows). The test now measures where the centred text
lies and checks the buttons, the Discord button and the links.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113uEh3VQ6et4zDMZ9cqzkf
This commit is contained in:
SofiaAFigueredo
2026-11-07 05:15:00 +07:00
committed by bflatastic
co-authored by Claude Sonnet 5.5
parent 3d31b623a4
commit 9b8b1229b6
5 changed files with 67 additions and 19 deletions
+5 -1
View File
@@ -31,7 +31,11 @@ fn home_content(app: &mut VectorcraftApp, ui: &mut Ui) {
ui.add_space(4.0);
ui.label(egui::RichText::new(tl!("Vector illustration — fast, open, scriptable.")).size(14.0).color(t.text_dim));
ui.add_space(22.0);
// `horizontal` fills the width, so the row's own width sets its left edge.
let gap = ui.spacing().item_spacing.x + 8.0;
let row = widgets::button_width(ui, tl!("New file")) + gap + widgets::button_width(ui, tl!("Open"));
ui.horizontal(|ui| {
ui.add_space(((ui.available_width() - row) / 2.0).max(0.0));
if widgets::primary_button(ui, tl!("New file")).clicked() {
app.run("file.newDialog", json!({})).ok();
}
@@ -60,7 +64,7 @@ fn home_content(app: &mut VectorcraftApp, ui: &mut Ui) {
ui.vertical_centered(|ui| {
ui.label(egui::RichText::new(tl!("Community")).font(theme::semibold(14.0)).color(t.text));
ui.add_space(10.0);
crate::community::links(app, ui);
crate::community::links_centered(app, ui);
});
}
+29 -12
View File
@@ -53,27 +53,44 @@ pub fn discord_button(app: &mut VectorcraftApp, ui: &mut Ui, large: bool) -> egu
resp
}
/// One link row: icon, label, opens the command's URL.
fn link(app: &mut VectorcraftApp, ui: &mut Ui, icon: &str, label: &str, cmd: &str, url: &str) {
/// One link row: icon, label, opens the command's URL. `centered` drops the icon so the label
/// alone sits in the middle of the column.
fn link(app: &mut VectorcraftApp, ui: &mut Ui, icon: &str, label: &str, cmd: &str, url: &str, centered: bool) {
let t = Tokens::get(ui.ctx());
ui.horizontal(|ui| {
icons::icon(ui, icon, 16.0, t.icon);
let r = ui.add(egui::Button::new(egui::RichText::new(label).color(t.accent).size(13.0)).frame(false)).on_hover_text(url);
if r.clicked() {
app.open_link(cmd);
}
});
let button = |ui: &mut Ui| ui.add(egui::Button::new(egui::RichText::new(label).color(t.accent).size(13.0)).frame(false)).on_hover_text(url);
let r = if centered {
button(ui)
} else {
ui.horizontal(|ui| {
icons::icon(ui, icon, 16.0, t.icon);
button(ui)
})
.inner
};
if r.clicked() {
app.open_link(cmd);
}
}
/// The Discord button and the website / app page / GitHub links.
pub fn links(app: &mut VectorcraftApp, ui: &mut Ui) {
links_in(app, ui, false);
}
/// [`links`] for a centred column (the Home screen's, as PhotoCraft's, #4684): the Discord button
/// and the links, each in the middle.
pub fn links_centered(app: &mut VectorcraftApp, ui: &mut Ui) {
links_in(app, ui, true);
}
fn links_in(app: &mut VectorcraftApp, ui: &mut Ui, centered: bool) {
discord_button(app, ui, true);
ui.add_space(8.0);
let l = app.session.execute("help.links", &serde_json::json!({})).unwrap_or_default();
let s = |k: &str| l[k].as_str().unwrap_or("").to_string();
link(app, ui, "globe", tl!("ArtCraft website"), "help.website", &s("website"));
link(app, ui, "external-link", tl!("VectorCraft on getartcraft.com"), "help.appPage", &s("appPage"));
link(app, ui, "git-branch", tl!("Source code on GitHub"), "help.github", &s("github"));
link(app, ui, "globe", tl!("ArtCraft website"), "help.website", &s("website"), centered);
link(app, ui, "external-link", tl!("VectorCraft on getartcraft.com"), "help.appPage", &s("appPage"), centered);
link(app, ui, "git-branch", tl!("Source code on GitHub"), "help.github", &s("github"), centered);
}
#[cfg(test)]
+25 -5
View File
@@ -440,11 +440,11 @@ fn a_queued_reopen_keeps_the_roots_it_was_asked_in() {
}
/// The x-centre of the first text shape whose text is `text` on a drawn Home screen, and the
/// window's centre (#4684).
/// window's centre (#4684). A centred galley's `pos` is its anchor, so its rect says where it lies.
fn text_centre_x(app: &mut VectorcraftApp, text: &str) -> Option<(f32, f32)> {
fn find(s: &egui::Shape, text: &str) -> Option<f32> {
match s {
egui::Shape::Text(t) if t.galley.text() == text => Some(t.pos.x + t.galley.size().x / 2.0),
egui::Shape::Text(t) if t.galley.text() == text => Some(t.pos.x + t.galley.rect.center().x),
egui::Shape::Vec(v) => v.iter().find_map(|s| find(s, text)),
_ => None,
}
@@ -460,12 +460,32 @@ fn text_centre_x(app: &mut VectorcraftApp, text: &str) -> Option<(f32, f32)> {
Some((x, screen.center().x))
}
/// The Home screen centres its welcome and its community block, as PhotoCraft's does (#4684).
/// The Home screen centres its welcome, its New / Open buttons and its community block, as
/// PhotoCraft's does (#4684).
#[test]
fn home_welcome_and_community_are_centred() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
for text in ["Welcome to VectorCraft", "Community"] {
for text in ["Welcome to VectorCraft", "Community", "ArtCraft website", "VectorCraft on getartcraft.com", "Source code on GitHub"] {
let (x, centre) = text_centre_x(&mut app, text).unwrap_or_else(|| panic!("{text} is drawn"));
assert!((x - centre).abs() < 2.0, "{text} is centred: {x} vs {centre}");
assert!((x - centre).abs() < 6.0, "{text} is centred: {x} vs {centre}");
}
// The Discord button is centred; its label lies right of its icon, 0.375 of the button's 36 pt
// height right of the middle.
let (x, centre) = text_centre_x(&mut app, "Join our Discord").unwrap();
assert!((x - centre - 0.375 * 36.0).abs() < 2.0, "the Discord button is centred: label at {x} about {centre}");
// The two buttons (a label's width plus 32, at least 72, each) lie symmetrically about the
// centre: the first one's left edge and the second one's right edge are equally far from it.
let width = |label: &str| {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut w = 0.0;
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| w = crate::widgets::button_width(ui, label));
out.textures_delta.clear();
w
};
let (new_x, centre) = text_centre_x(&mut app, "New file").unwrap();
let (open_x, _) = text_centre_x(&mut app, "Open").unwrap();
let left = new_x - width("New file") / 2.0;
let right = open_x + width("Open") / 2.0;
assert!((centre - left - (right - centre)).abs() < 6.0, "New / Open row is centred: {left}..{right} about {centre}");
}
+1 -1
View File
@@ -28,7 +28,7 @@ pub use buttons::{
SPLIT_ARROW, flat_button, flat_button_fit, icon_button, icon_button_enabled, menu_item, menu_item_name, paint_icon_button, primary_button,
primary_button_enabled, secondary_button, split_button, split_button_named, toggle_icon,
};
pub(crate) use buttons::{icon_button_info, primary_button_width, secondary_button_width};
pub(crate) use buttons::{button_width, icon_button_info, primary_button_width, secondary_button_width};
#[cfg(test)]
use chips::gradient_chip_colors;
pub use chips::{ProxyClicks, chip_button, chip_button_named, fill_stroke_proxy, gradient_chip, paint_chip, proxy_chip, swatch_tile};
+7
View File
@@ -101,6 +101,13 @@ fn flat_fill(ui: &Ui, rect: Rect, resp: &Response) {
}
}
/// The width [`primary_button`] and [`secondary_button`] give `text` (their label plus padding, at
/// least 72), for centring a row of them.
pub(crate) fn button_width(ui: &Ui, text: &str) -> f32 {
let galley = ui.painter().layout_no_wrap(tl!(text).to_string(), theme::semibold(12.5), Color32::PLACEHOLDER);
(galley.size().x + 32.0).max(72.0)
}
/// The accent call-to-action button (dialog OK/Create).
pub fn primary_button(ui: &mut Ui, text: &str) -> Response {
primary_button_enabled(ui, text, true)