From ab0d8b6d77aee15de9feb5fc368f99156968e980 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 18:06:39 -0800 Subject: [PATCH] Home: a Learn section lists the tutorials and the Quick skill guide The Home screen gets a Learn section between Recent Files and the new-file presets: the Quick skill guide card, which shows the Learn panel's list of skills (learn.skills), and each tutorial under its skill, which shows the panel at its steps (learn.show). It works with no document open and over open documents, opens no document and never hides the panel. The card is shared with the panel's For you tab and now shows an outline while it has the keyboard focus. --- crates/ui-egui/src/canvas/home.rs | 37 ++++++++++++- crates/ui-egui/src/panels/learn.rs | 19 ++++++- crates/ui-egui/src/tests/home.rs | 88 +++++++++++++++++++++++++++++- docs/mcp.md | 8 +++ docs/target-app-parity.md | 1 + 5 files changed, 148 insertions(+), 5 deletions(-) diff --git a/crates/ui-egui/src/canvas/home.rs b/crates/ui-egui/src/canvas/home.rs index cd2ac9c44a..bffab8f06c 100644 --- a/crates/ui-egui/src/canvas/home.rs +++ b/crates/ui-egui/src/canvas/home.rs @@ -22,7 +22,7 @@ pub(super) fn home(app: &mut VectorcraftApp, ui: &mut Ui, rect: egui::Rect) { }); } -/// The Home screen's column: welcome, New / Open, Recent Files, presets and community links. +/// The Home screen's column: welcome, New / Open, Recent Files, Learn, presets and community links. fn home_content(app: &mut VectorcraftApp, ui: &mut Ui) { let t = Tokens::get(ui.ctx()); // The welcome and the New / Open buttons sit centred, as PhotoCraft's Home screen does (#4684). @@ -46,6 +46,7 @@ fn home_content(app: &mut VectorcraftApp, ui: &mut Ui) { }); }); recent(app, ui); + learn(app, ui); ui.add_space(28.0); ui.label(egui::RichText::new(tl!("Quickly start a new file")).font(theme::semibold(14.0)).color(t.text)); ui.add_space(10.0); @@ -68,6 +69,40 @@ fn home_content(app: &mut VectorcraftApp, ui: &mut Ui) { }); } +/// The id of the Home screen's Quick skill guide card. +const LEARN_GUIDE_ID: &str = "home-quick-skill-guide"; + +/// The Home screen's Learn section: the Quick skill guide card, which shows the Learn panel's list +/// of skills, and the Learn panel's tutorials under their skills, each a button that shows the +/// panel at its steps (`learn.show`). Neither opens, closes or switches a document, and the panel +/// shows beside the Home screen. +fn learn(app: &mut VectorcraftApp, ui: &mut Ui) { + use crate::panels::learn::{catalog, guide_card}; + let t = Tokens::get(ui.ctx()); + ui.add_space(28.0); + ui.label(egui::RichText::new(tl!("Learn")).font(theme::semibold(14.0)).color(t.text)); + ui.add_space(10.0); + guide_card(app, ui, egui::Id::unique(LEARN_GUIDE_ID), t.panel); + let mut open = None; + for skill in catalog::SKILLS { + ui.add_space(10.0); + ui.label(egui::RichText::new(skill.title).size(12.0).color(t.text_dim)); + ui.add_space(4.0); + ui.horizontal_wrapped(|ui| { + for tutorial in skill.tutorials { + if widgets::secondary_button(ui, tutorial.title).clicked() { + open = Some(tutorial.id); + } + } + }); + } + if let Some(id) = open + && let Err(e) = app.run("learn.show", json!({ "tutorial": id })) + { + app.status(e); + } +} + /// The Home screen's Recent Files (#663): the first of File › Open Recent Files, each its name and /// folder; a click opens it. Nothing when there are none. fn recent(app: &mut VectorcraftApp, ui: &mut Ui) { diff --git a/crates/ui-egui/src/panels/learn.rs b/crates/ui-egui/src/panels/learn.rs index ee7967c8a3..5d24167c99 100644 --- a/crates/ui-egui/src/panels/learn.rs +++ b/crates/ui-egui/src/panels/learn.rs @@ -8,7 +8,8 @@ //! next tutorial in the same skill. `learn.list` lists the tutorials, `learn.skills` shows the list //! of skills, `learn.show` opens one tutorial, `learn.locateTool` shows where a tool is, //! `learn.openSample`, `learn.finish` and `learn.next` run the three practice controls, and -//! `window.learn` shows or hides the panel. +//! `window.learn` shows or hides the panel. The Home screen's Learn section shows the same card +//! ([`guide_card`]) and the tutorials, which open the panel. pub mod catalog; pub mod locate; @@ -68,8 +69,16 @@ fn for_you(app: &mut VectorcraftApp, ui: &mut Ui) { let t = Tokens::get(ui.ctx()); crate::widgets::subheader(ui, tl!("Get started")); ui.add_space(4.0); + guide_card(app, ui, ui.scope_id().with("quick-skill-guide"), t.panel_darker); +} + +/// The Quick skill guide card, filled with `fill`, as one button with the id `id`: a click, or Enter +/// or Space while it has the keyboard focus, shows the panel's list of skills (`learn.skills`). The +/// For you tab and the Home screen's Learn section show it. +pub(crate) fn guide_card(app: &mut VectorcraftApp, ui: &mut Ui, id: egui::Id, fill: egui::Color32) { + let t = Tokens::get(ui.ctx()); let card = egui::Frame::NONE - .fill(t.panel_darker) + .fill(fill) .stroke(Stroke::new(1.0, t.border)) .corner_radius(egui::CornerRadius::same(t.radius)) .inner_margin(egui::Margin::same(10)) @@ -86,8 +95,12 @@ fn for_you(app: &mut VectorcraftApp, ui: &mut Ui) { .color(t.text_dim), )); }); - let resp = ui.interact(card.response.rect, ui.scope_id().with("quick-skill-guide"), Sense::click()); + let resp = ui.interact(card.response.rect, id, Sense::click()); resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, ui.is_enabled(), tl!("Quick skill guide"))); + if resp.has_focus() { + ui.painter().rect_stroke(resp.rect, egui::CornerRadius::same(t.radius), Stroke::new(1.0, t.selection), egui::StrokeKind::Inside); + } + let resp = resp.on_hover_cursor(egui::CursorIcon::PointingHand); if resp.clicked() && let Err(e) = app.run("learn.skills", json!({})) { diff --git a/crates/ui-egui/src/tests/home.rs b/crates/ui-egui/src/tests/home.rs index d1c20991af..6cfdf78f38 100644 --- a/crates/ui-egui/src/tests/home.rs +++ b/crates/ui-egui/src/tests/home.rs @@ -299,6 +299,91 @@ fn home_scrolls_in_a_short_window() { assert_eq!(app.session.documents().len(), 1, "the card opened its preset"); } +/// The visible text `text` on a tall Home screen frame drawn at `time` → its center. +fn learn_text_at(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64, text: &str) -> egui::Pos2 { + let texts = visible_home_texts(app, ctx, (1000.0, 2400.0), time, vec![]); + texts.iter().find(|(t, _)| t == text).map(|(_, r)| r.center()).unwrap_or_else(|| panic!("{text} is shown: {texts:?}")) +} + +/// A click at `at` on a tall Home screen frame, pressed at `time` and released a frame later. +fn click_home_at(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64, at: egui::Pos2) { + let press = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + visible_home_texts(app, ctx, (1000.0, 2400.0), time, vec![egui::Event::PointerMoved(at), press(true)]); + visible_home_texts(app, ctx, (1000.0, 2400.0), time + 0.05, vec![press(false)]); +} + +/// The Home screen has a Learn section: the Quick skill guide card and every tutorial of the Learn +/// panel under its skill. A tutorial shows the Learn panel at its steps and the card shows the list +/// of skills; a second choice keeps the panel shown, and no document is opened. +#[test] +fn home_has_a_learn_section_that_opens_the_learn_panel() { + use crate::panels::learn::{self, Tab, View, catalog}; + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default()); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let texts = visible_home_texts(&mut app, &ctx, (1000.0, 2400.0), 0.0, vec![]); + let has = |s: &str| texts.iter().any(|(t, _)| t == s); + assert!(has("Learn") && has("Quick skill guide"), "{texts:?}"); + for skill in catalog::SKILLS { + assert!(has(skill.title), "{} is shown", skill.title); + for tutorial in skill.tutorials { + assert!(has(tutorial.title), "{} is shown", tutorial.title); + } + } + assert!(!learn::visible(&app.ui), "the panel is not shown before a choice"); + let at = learn_text_at(&mut app, &ctx, 0.5, "Draw paths with the Pen"); + click_home_at(&mut app, &ctx, 1.0, at); + assert!(learn::visible(&app.ui), "a tutorial shows the Learn panel"); + assert_eq!(app.ui.learn, View { tab: Tab::Skills, skill: Some("draw"), tutorial: Some("pen-paths") }); + assert!(app.session.documents().is_empty(), "no document is opened"); + assert!(crate::menus::home_showing(&app), "Home stays"); + let at = learn_text_at(&mut app, &ctx, 1.5, "Quick skill guide"); + click_home_at(&mut app, &ctx, 2.0, at); + assert!(learn::visible(&app.ui), "a second choice keeps the panel shown"); + assert_eq!(app.ui.learn, View { tab: Tab::Skills, skill: None, tutorial: None }, "the card shows the list of skills"); + assert!(app.session.documents().is_empty()); +} + +/// Home shown over open documents has the Learn section too; a tutorial chosen there leaves the +/// documents and the active one as they are. +#[test] +fn home_learn_section_over_open_documents_leaves_them_as_they_are() { + use crate::panels::learn::{self, Tab, View}; + let mut app = app_with_doc(); + app.run("file.new", json!({})).unwrap(); + app.run("document.activate", json!({"index": 0})).unwrap(); + app.run("app.home", json!({})).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let at = learn_text_at(&mut app, &ctx, 0.0, "Add a gradient"); + click_home_at(&mut app, &ctx, 0.5, at); + assert!(learn::visible(&app.ui)); + assert_eq!(app.ui.learn, View { tab: Tab::Skills, skill: Some("color"), tutorial: Some("gradient") }); + assert_eq!((app.session.documents().len(), app.session.active_index()), (2, Some(0)), "the documents stay as they are"); + assert!(crate::menus::home_showing(&app), "Home stays over the documents"); +} + +/// The Home screen's Quick skill guide card works from the keyboard: with the keyboard focus, Enter +/// shows the Learn panel's list of skills. +#[test] +fn home_quick_skill_guide_card_works_from_the_keyboard() { + use crate::panels::learn::{self, Tab, View}; + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default()); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + visible_home_texts(&mut app, &ctx, (1000.0, 2400.0), 0.0, vec![]); + let card = egui::Id::unique("home-quick-skill-guide"); + ctx.memory_mut(|m| m.request_focus(card)); + visible_home_texts(&mut app, &ctx, (1000.0, 2400.0), 0.1, vec![]); + assert!(ctx.memory(|m| m.has_focus(card)), "the card takes the keyboard focus"); + let key = |pressed| egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed, repeat: false, modifiers: Default::default() }; + visible_home_texts(&mut app, &ctx, (1000.0, 2400.0), 0.2, vec![key(true)]); + visible_home_texts(&mut app, &ctx, (1000.0, 2400.0), 0.3, vec![key(false)]); + assert!(learn::visible(&app.ui)); + assert_eq!(app.ui.learn, View { tab: Tab::Skills, skill: None, tutorial: None }); + assert!(app.session.documents().is_empty()); +} + /// A file that is already open is not opened again, however it is asked for: Home › Recent Files, /// File › Open Recent, File › Open, the Finder and a drop all focus its tab, unsaved edits and all /// (#3455). @@ -452,7 +537,8 @@ fn text_centre_x(app: &mut VectorcraftApp, text: &str) -> Option<(f32, f32)> { app.canvas_rect = None; let ctx = egui::Context::default(); crate::theme::install_fonts(&ctx); - let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1000.0, 900.0)); + // Tall enough to draw the whole screen, Learn section included. + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1000.0, 1600.0)); let raw = egui::RawInput { screen_rect: Some(screen), ..Default::default() }; let mut out = ctx.run_ui(raw, |ui| canvas::show(app, ui)); out.textures_delta.clear(); diff --git a/docs/mcp.md b/docs/mcp.md index f7d99cbd04..788ed6fe4e 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2880,6 +2880,14 @@ not finished, or a skill's last tutorial, it is an error. For all three, a missi is an error, and an error changes nothing. Behavior reference: [Quick skill guide](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html). +The Home screen has a Learn section between Recent Files and the new-file presets, as +[the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/homescreen-overview.html) +lists Learn, with in-app tutorials, among the Home screen's options. The section holds the Quick skill guide card, +which runs `learn.skills`, and every tutorial of the Learn panel under its skill, each a button that runs +`learn.show {tutorial}`. The panel shows beside the Home screen; no document is opened, closed or switched, and a +second choice keeps the panel shown. The section's place, its layout and what a choice opens are VectorCraft's own +rules. + ```json {"name":"invoke_menu","arguments":{"command":"learn.show","params":{"tutorial":"pen-paths"}}} {"name":"invoke_menu","arguments":{"command":"learn.openSample","params":{"tutorial":"pen-paths"}}} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 19b9c8d319..e4a0c221e5 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -910,6 +910,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - The Learn panel (Window › Learn, or Explore tutorials beside the search icon on the app bar) has a For you tab with a Get started section and a Quick skill guide card, and a Skills tab that lists VectorCraft's own skills with their tutorial counts and empty progress rings; a skill lists its tutorials, a tutorial shows its numbered steps, and Back goes up a level; `learn.list` and `learn.show {tutorial}` script it ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `panels::learn::tests`). - A tool's name in a Learn tutorial step is a blue link that shows where the tool is without choosing it (`learn.locateTool {tool}`): its toolbar button is marked, a collapsed group's flyout opens with the tool's row marked, a torn-off strip comes to the front, and a tool no shown toolbar holds is marked in the All Tools drawer. The mark, its 3 seconds and the hidden-toolbar cases are VectorCraft's own rules ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `panels::learn::tests`, the `a_learn_link_*` tests in `toolbar::tests`). - Under a tutorial's steps, View more opens the tutorial's own practice sample as a new untitled document, and once the tutorial is finished, Next Up moves to the next tutorial in the same skill, as the help page describes ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html)). The samples are VectorCraft's own drawings. A Finish tutorial button that shows once the sample is open, the untitled copy, Back to skills in place of Next Up after a skill's last tutorial, and Next Up showing the next tutorial without opening its sample are VectorCraft's own rules. `learn.openSample`, `learn.finish`, `learn.next` and `learn.skills` (Back to skills) script them. Not done: the help page's short video, and progress kept between sessions and shown in the skill rings. Regression tests: `view_more_opens_the_tutorial_s_sample_as_a_new_untitled_document`, `finishing_a_tutorial_offers_next_up_in_its_skill`, `the_practice_controls_work_from_the_keyboard`, `the_last_tutorial_of_a_skill_offers_back_to_skills`, `learn_finish_and_learn_next_follow_the_practice`, `learn_skills_shows_the_list_of_skills`, `the_practice_commands_reject_a_missing_or_unknown_tutorial`, `every_tutorial_has_its_own_readable_sample` and `the_next_tutorial_is_the_next_one_in_the_same_skill` in `crates/ui-egui/src/panels/learn/tests.rs`. + - The Home screen has a Learn section with the in-app tutorials, as the help page lists among the Home screen's options ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/homescreen-overview.html)): the Quick skill guide card shows the Learn panel's list of skills (`learn.skills`), and each tutorial, listed under its skill, shows the panel at its steps (`learn.show`). It shows with no document open and over open documents, and a choice leaves the documents as they are. Its place between Recent Files and the new-file presets, its layout and what a choice opens are VectorCraft's own rules (`tests::home::home_has_a_learn_section_that_opens_the_learn_panel`, `home_learn_section_over_open_documents_leaves_them_as_they_are`, `home_quick_skill_guide_card_works_from_the_keyboard`). - The errors of the blend spine commands (Move Spine Point, Reshape Spine Segment, Add Spine Point and Delete Spine Point) show in the status bar in the interface language: each has a row in every catalog that translates all status and error messages and in Bulgarian (`blend_spine_param_errors_are_translated`). - The errors of the 3D effects (Extrude & Bevel, Revolve, Inflate, Rotate and their Classic forms) show in the status bar in the interface language: the art a 3D effect cannot build a solid from, invalid light, graphics and surface settings, and the notes for an object that draws no 3D faces or more wireframe edges than the limit. Each has a row in every catalog that translates all status and error messages, and the effect's name and the values in the message are translated or kept as written (`three_d_refusals_are_translated_in_every_complete_catalog`, `three_d_refusals_translate_with_their_values`). - The errors of the parameter checks that many commands share show in the status bar in the interface language: a value that is not a number where a number goes (`object.move` with `"dx": "60 mm"`, and `object.rotate`, `object.setBounds` and the other commands that call `check_numbers`), a missing number or index, `ids` that are not a list of object ids, an object id that is not a non-negative integer or names no object, and a date that is not Unix seconds. Each has a row in every catalog that translates all status and error messages, and in Bulgarian, Danish, Indonesian and Dutch (`shared_param_check_errors_are_translated`).