mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-09 20:09:38 -06:00
Replace the Account page with About (#1697)
* UI: replace Account backstage page with About * UI: stack About columns in narrow windows; keep PowerPoint's Account in parity doc * UI: About review fixes: stack columns below 860pt, Help icon, Credits task; doc headers
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
**Stage: alpha** · next: beta, ~20 points and ~120–210 h away
|
||||
|
||||
> **Last reviewed:** 2026-11-02 · **Last updated:** 2026-11-02 · **Change:** trivial (File › About replaces Account; product links, credits and appearance retained; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-11-01 · **Change:** trivial (locked No Fill and no-outline click-through regression coverage added; No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-30 · **Change:** trivial (progress log: read-only `slide.actions` lists shape and table text actions; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-27 · **Change:** trivial (progress log: Set Up Slide Show ▸ Keep mouse cursor visible, #1445; stage and scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
@@ -148,6 +149,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-11-02 | File › About replaces the unused Account page, with version, project links, contributor/model credits and appearance preferences |
|
||||
| 2026-10-31 | No Fill shape interiors allow selection and editing of underlying objects, even when a quick style remains; hit-testing uses the renderer’s fill resolution. Outlines, rotation, text targets and actual fills keep their hit behaviour (#1621) |
|
||||
| 2026-10-30 | Print-preview tests use the URL emitted by egui and its exact file path; previews are explicitly removed before the macOS process exits |
|
||||
| 2026-10-30 | `slide.actions` lists shape click and hover actions and text-run hyperlinks (including table cells and grouped shapes) for audit; listing never runs an action. The UI pane is a follow-up |
|
||||
@@ -319,6 +321,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-02 | trivial | File › About replaces Account; product links, credits and appearance retained |
|
||||
| 2026-10-31 | trivial | No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged |
|
||||
| 2026-10-30 | trivial | Progress log: read-only slide action inspection; parity scores unchanged |
|
||||
| 2026-10-29 | trivial | Progress log: going back in the slide show reverses the left slide's transition (#1531). Scores and milestone names unchanged |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! bar until Esc or the back arrow returns to the presentation. With nothing open it is the start
|
||||
//! screen: PowerPoint's start screen and File › Home are the same page.
|
||||
//!
|
||||
//! A side menu on the left picks a page (Home, New, Open, Info, Save As, Print, Export, Account);
|
||||
//! A side menu on the left picks a page (Home, New, Open, Info, Save As, Print, Export, About);
|
||||
//! Save, Close and Options act at once. Every action runs an existing command through
|
||||
//! [`SlideApp::run`] or one of the app's file helpers ([`SlideApp::export_as`],
|
||||
//! [`SlideApp::print_pdf`], [`SlideApp::save_as_dialog`]); the state here is UI-only and never
|
||||
@@ -24,7 +24,7 @@ mod print;
|
||||
|
||||
pub use files::split_path;
|
||||
use files::{home, new_page, open_page};
|
||||
use info::{PROPERTIES, account, commit_props, info, save_as};
|
||||
use info::{PROPERTIES, about, commit_props, info, save_as};
|
||||
pub use info::{SAVE_TYPES, format_bytes};
|
||||
use print::{export, print};
|
||||
pub use print::{print_page_size, print_params, print_slides};
|
||||
@@ -47,11 +47,11 @@ pub enum Page {
|
||||
SaveAs,
|
||||
Print,
|
||||
Export,
|
||||
Account,
|
||||
About,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub const ALL: [Page; 8] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::Account];
|
||||
pub const ALL: [Page; 8] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::About];
|
||||
|
||||
/// The id `app.backstage {page}` takes.
|
||||
pub fn id(self) -> &'static str {
|
||||
@@ -63,12 +63,16 @@ impl Page {
|
||||
Page::SaveAs => "saveAs",
|
||||
Page::Print => "print",
|
||||
Page::Export => "export",
|
||||
Page::Account => "account",
|
||||
Page::About => "about",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(id: &str) -> Option<Page> {
|
||||
let id = id.trim();
|
||||
// Keep existing control-channel callers working after the visible page rename.
|
||||
if id.eq_ignore_ascii_case("account") {
|
||||
return Some(Page::About);
|
||||
}
|
||||
Page::ALL.into_iter().find(|p| p.id().eq_ignore_ascii_case(id))
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ impl Page {
|
||||
Page::SaveAs => "Save As",
|
||||
Page::Print => "Print",
|
||||
Page::Export => "Export",
|
||||
Page::Account => "Account",
|
||||
Page::About => "About",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +99,7 @@ impl Page {
|
||||
Page::SaveAs => Icon::SaveAs,
|
||||
Page::Print => Icon::Print,
|
||||
Page::Export => Icon::Export,
|
||||
Page::Account => Icon::Account,
|
||||
Page::About => Icon::Help,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +144,7 @@ pub fn entries(has_doc: bool) -> (Vec<Nav>, Vec<Nav>) {
|
||||
if has_doc {
|
||||
top.extend([Nav::Page(Page::Info), Nav::Save, Nav::Page(Page::SaveAs), Nav::Page(Page::Print), Nav::Page(Page::Export), Nav::Close]);
|
||||
}
|
||||
(top, vec![Nav::Page(Page::Account), Nav::Options])
|
||||
(top, vec![Nav::Page(Page::About), Nav::Options])
|
||||
}
|
||||
|
||||
/// Print page: which slides.
|
||||
@@ -412,7 +416,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) {
|
||||
Page::SaveAs => save_as(app, ui, &mut bs, &mut out),
|
||||
Page::Print => print(app, ui, &mut bs),
|
||||
Page::Export => export(app, ui, &mut bs),
|
||||
Page::Account => account(app, ui),
|
||||
Page::About => about(app, ui),
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,11 +189,30 @@ pub(super) fn save_as(app: &mut SlideApp, ui: &mut Ui, bs: &mut Backstage, out:
|
||||
}
|
||||
}
|
||||
|
||||
/// Account: the interface's appearance and what DeckCraft is. DeckCraft has no accounts, so
|
||||
/// there is nothing to sign in to.
|
||||
pub(super) fn account(app: &mut SlideApp, ui: &mut Ui) {
|
||||
/// Product information, project links, credits and appearance preferences.
|
||||
pub(super) fn about(app: &mut SlideApp, ui: &mut Ui) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
ui.horizontal_top(|ui| {
|
||||
// Side by side needs 480 + 40 + 340 points; stack the columns when the page is narrower.
|
||||
let layout = if ui.available_width() >= 860.0 { egui::Layout::left_to_right(egui::Align::Min) } else { egui::Layout::top_down(egui::Align::Min) };
|
||||
ui.with_layout(layout, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_width(480.0);
|
||||
heading(ui, "Product Information");
|
||||
ui.horizontal(|ui| {
|
||||
let (r, _) = ui.allocate_exact_size(vec2(40.0, 40.0), Sense::hover());
|
||||
crate::dialogs::paint_logo(ui.painter(), r);
|
||||
ui.label(RichText::new("DeckCraft").font(theme::bold(24.0)).color(t.text));
|
||||
});
|
||||
dim(ui, &crate::dialogs::version_line());
|
||||
dim(ui, "Open source under MIT OR Apache-2.0. Not affiliated with Microsoft.");
|
||||
ui.add_space(14.0);
|
||||
if task(ui, Icon::Help, "Credits", "Credits", "The people and models who built DeckCraft.", true) {
|
||||
let _ = app.run("app.about", json!({}));
|
||||
}
|
||||
ui.hyperlink_to("Join our community on Discord", deckcraft_engine::links::DISCORD);
|
||||
ui.hyperlink_to("Source on GitHub", deckcraft_engine::links::GITHUB);
|
||||
});
|
||||
ui.add_space(40.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.set_width(340.0);
|
||||
heading(ui, "Appearance");
|
||||
@@ -210,33 +229,6 @@ pub(super) fn account(app: &mut SlideApp, ui: &mut Ui) {
|
||||
})
|
||||
.response
|
||||
.widget_info(|| egui::WidgetInfo::labeled(egui::Role::ComboBox, true, "Appearance"));
|
||||
ui.add_space(16.0);
|
||||
heading(ui, "User Information");
|
||||
dim(ui, "DeckCraft has no accounts: there is nothing to sign in to, and your presentations stay on your device.");
|
||||
});
|
||||
ui.add_space(40.0);
|
||||
ui.vertical(|ui| {
|
||||
heading(ui, "Product Information");
|
||||
ui.horizontal(|ui| {
|
||||
let (r, _) = ui.allocate_exact_size(vec2(40.0, 40.0), Sense::hover());
|
||||
crate::dialogs::paint_logo(ui.painter(), r);
|
||||
ui.label(RichText::new("DeckCraft").font(theme::bold(24.0)).color(t.text));
|
||||
});
|
||||
dim(ui, &crate::dialogs::version_line());
|
||||
dim(ui, "Open source under MIT OR Apache-2.0. Not affiliated with Microsoft.");
|
||||
ui.add_space(14.0);
|
||||
if task(
|
||||
ui,
|
||||
Icon::Help,
|
||||
"About DeckCraft",
|
||||
"About DeckCraft",
|
||||
"The version, the people and models who built it, and where to find us.",
|
||||
true,
|
||||
) {
|
||||
let _ = app.run("app.about", json!({}));
|
||||
}
|
||||
ui.hyperlink_to("Join our community on Discord", deckcraft_engine::links::DISCORD);
|
||||
ui.hyperlink_to("Source on GitHub", deckcraft_engine::links::GITHUB);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,11 +85,13 @@ fn page_ids_round_trip_and_unknown_ids_are_refused() {
|
||||
assert_eq!(Page::parse(p.id()), Some(p));
|
||||
}
|
||||
assert_eq!(Page::parse(" SAVEAS "), Some(Page::SaveAs));
|
||||
assert_eq!(Page::parse("account"), Some(Page::About));
|
||||
assert_eq!(Page::parse("about"), Some(Page::About));
|
||||
assert_eq!(Page::parse("share"), None);
|
||||
assert_eq!(Page::parse(""), None);
|
||||
let (top, bottom) = entries(false);
|
||||
assert_eq!(top, vec![Nav::Page(Page::Home), Nav::Page(Page::New), Nav::Page(Page::Open)], "the start screen has no presentation pages");
|
||||
assert_eq!(bottom, vec![Nav::Page(Page::Account), Nav::Options]);
|
||||
assert_eq!(bottom, vec![Nav::Page(Page::About), Nav::Options]);
|
||||
assert_eq!(entries(true).0.len(), 9);
|
||||
assert_eq!(split_path("/home/me/Talk.pptx"), ("Talk.pptx".into(), "/home/me".into()));
|
||||
assert_eq!(format_bytes(512), "512 bytes");
|
||||
@@ -166,7 +168,7 @@ fn every_page_renders() {
|
||||
(Page::SaveAs, "Not saved yet"),
|
||||
(Page::Print, "Print Hidden Slides"),
|
||||
(Page::Export, "Create PDF Document"),
|
||||
(Page::Account, "About DeckCraft"),
|
||||
(Page::About, "Credits"),
|
||||
];
|
||||
for (p, marker) in checks {
|
||||
h.state_mut().run("app.backstage", json!({"page": p.id()})).unwrap();
|
||||
@@ -704,3 +706,40 @@ fn options_in_backstage_opens_preferences_and_cancel_closes_it() {
|
||||
assert!(h.state().dialog.is_none(), "Cancel closes Preferences dialog");
|
||||
assert!(h.state().backstage_open(), "Backstage remains open");
|
||||
}
|
||||
|
||||
/// About replaces the account entry and still opens the credits dialog.
|
||||
#[test]
|
||||
fn about_has_product_links_and_credits_without_account_controls() {
|
||||
let mut h = harness(new_app());
|
||||
h.get_by_label("File").click();
|
||||
h.run_steps(3);
|
||||
assert!(h.query_by_label("Account").is_none());
|
||||
click_nav(&h, "About");
|
||||
h.run_steps(3);
|
||||
assert_eq!(page(&h), Some(Page::About));
|
||||
assert!(h.query_by_label("User Information").is_none());
|
||||
assert!(h.query_by_label("Source on GitHub").is_some());
|
||||
assert!(h.query_by_label("Join our community on Discord").is_some());
|
||||
assert!(h.query_all_by_label("Appearance").next().is_some());
|
||||
click_on_page(&h, "Credits");
|
||||
h.run_steps(3);
|
||||
assert!(h.state().dialog.is_some());
|
||||
assert!(h.query_by_label("Contributors").is_some());
|
||||
assert!(h.query_by_label("Models").is_some());
|
||||
}
|
||||
|
||||
/// At the 900-point minimum window width the About columns stack and Appearance stays on the page.
|
||||
#[test]
|
||||
fn about_appearance_stays_on_the_page_at_minimum_width() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(900.0, 900.0)).build_ui_state(
|
||||
|ui, app: &mut SlideApp| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
},
|
||||
new_app(),
|
||||
);
|
||||
h.state_mut().run("app.backstage", json!({"page": "about"})).unwrap();
|
||||
h.run_steps(3);
|
||||
let r = h.query_all_by_role_and_label(Role::ComboBox, "Appearance").next().expect("Appearance selector").rect();
|
||||
assert!(r.right() <= 900.0, "Appearance selector ends at {}, inside the window", r.right());
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
|
||||
("show.end", "End Show", None, "{}"),
|
||||
("animation.preview", "Preview Animations", None, "{on?: bool, from?: effect index}"),
|
||||
("app.home", "Home", None, "{on?}"),
|
||||
("app.backstage", "File", Some("Alt+F"), "{page?: home|new|open|info|saveAs|print|export|account, on?: bool}"),
|
||||
("app.backstage", "File", Some("Alt+F"), "{page?: home|new|open|info|saveAs|print|export|about, on?: bool}"),
|
||||
("app.closePresentation", "Close", Some("Cmd+W"), "{}"),
|
||||
(
|
||||
"app.print",
|
||||
|
||||
+6
-3
@@ -1,5 +1,6 @@
|
||||
# UI parity with PowerPoint
|
||||
|
||||
> **Last reviewed:** 2026-11-02 · **Last updated:** 2026-11-02 · **Change:** trivial (File › About replaces Account; product links, credits and appearance retained; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-11-01 · **Change:** trivial (locked No Fill and no-outline click-through regression coverage added; No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-26 · **Change:** trivial (dragging several selected slides moves them all; previous: trivial, Edit Points: Shift, Ctrl/Cmd or Alt/Option on a handle drag sets the point type; previous: trivial, scroll bars on the zoomed slide pane, #1157; previous: major, community PR wave 2026-10-14: 14 new dialogs (41 in all), Eyedropper, Export button, New Window, Grid Options, WordArt gallery and handles, Slide Sorter sections, Quick Access Toolbar, Format Background and Format Shape depth, rich-text clipboard; duplicated rows and headers folded; previous: minor, File tab / Backstage, #76) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-22 · **Change:** trivial (group fill and outline edits reach members, #1079; scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
@@ -28,7 +29,7 @@ ends and are omitted from thumbnails, slide shows and exports.
|
||||
| Colour menus: theme and standard colours, hex field, Eyedropper | yes (plus a More Colors dialog) | yes: theme and standard colours, hex field and an Eyedropper that takes the colour drawn under the pointer on the slide (#367) | More Colors dialog; Eyedropper only takes colours from the slide, not the rest of the screen |
|
||||
| Contextual tabs: Shape Format, Picture Format, Table Design, Table Layout, Chart Design, Chart Format, Video/Audio Format and Playback | yes | most; checklist P0 row partial | Chart Format, media tabs thinner |
|
||||
| File tab (Backstage): Home, New, Open, Info, Save, Save As, Print, Share, Export, Close, Account, Options | yes | yes (#76), see [File tab](#file-tab-backstage); no Share (cloud) | Video/GIF/CD exports, templates beyond the built-in themes |
|
||||
| Start screen: new, themes, templates, recent, open | yes, the same page as File › Home | yes: File › Home with Home, New, Open, Account and Options (#76) | |
|
||||
| Start screen: new, themes, templates, recent, open | yes, the same page as File › Home | yes: File › Home with Home, New, Open, About and Options (#76) | |
|
||||
| Status bar: slide counter, language, notes, comments, views, zoom slider | yes | yes | |
|
||||
| Command search (Tell Me) | yes | command palette | |
|
||||
| Light / dark appearance | follows the system | light or dark; doesn't follow the system (#41) | 1–2 h |
|
||||
@@ -45,7 +46,8 @@ Close and, at the bottom under a rule, Account and Options. Entries are 40 point
|
||||
outline icon; the current page is drawn in the accent colour and outlined. Esc goes back. Alt+F opens
|
||||
on Home with the keyboard in the side menu; Up and Down move through it and a page shows as soon as
|
||||
its entry has the focus. PowerPoint's start screen is the same Home page, with only Home, New and
|
||||
Open at the top. DeckCraft follows all of this (`crates/ui-egui/src/backstage.rs`), with the page and
|
||||
Open at the top. DeckCraft follows all of this (`crates/ui-egui/src/backstage.rs`), except that
|
||||
Account is About, with the page and
|
||||
menu colours taken from the theme tokens (menu `chrome`, page `card`).
|
||||
|
||||
| Page | PowerPoint | DeckCraft |
|
||||
@@ -60,7 +62,7 @@ menu colours taken from the theme tokens (menu `chrome`, page `card`).
|
||||
| Share | Cloud sharing | Not offered |
|
||||
| Export | Create PDF/XPS Document, Create a Video, Create an Animated GIF, Package Presentation for CD, Create Handouts, Change File Type | Create PDF Document, Create Handouts (a PDF), Change File Type (PNG with an optional transparent background, #784). No video, GIF or CD package |
|
||||
| Close | Closes; asks Save / Don't Save / Cancel about unsaved changes | Same (`file.close` after the prompt); Ctrl+W asks too |
|
||||
| Account | User information, sign-in, Office Background and Office Theme, product information, About | Appearance (light/dark, `view.dark`), product information, About DeckCraft. No accounts to sign in to |
|
||||
| Account | User information, sign-in, Office Background and Office Theme, product information, About | About: product information, project links, contributor/model credits and appearance (light/dark, `view.dark`). No Account entry |
|
||||
| Options | Opens the PowerPoint Options dialog | Opens Preferences |
|
||||
|
||||
## Panes and dialogs
|
||||
@@ -171,6 +173,7 @@ consistency is open (#73, #56).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-02 | trivial | File › About replaces Account; product links, credits and appearance retained |
|
||||
| 2026-10-31 | trivial | No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged |
|
||||
| 2026-10-29 | trivial | Picture Format: the Picture Styles row shows as many styles as fit beside the groups after it (at least two) and a More arrow with all 24, as the Transitions and Animations galleries do; Crop and Size are in view in the default window (#1232) |
|
||||
| 2026-10-26 | trivial | Thumbnail pane and Slide Sorter: dragging one of several selected slides moves the whole selection |
|
||||
|
||||
Reference in New Issue
Block a user