Merge pull request #7771 from dotCooCoo/feat/status-bar-show-menu

Status bar: a Show menu chooses what the info field displays
This commit is contained in:
BFlat
2026-11-15 19:45:09 +07:00
committed by GitHub
40 changed files with 590 additions and 13 deletions
Generated
+1
View File
@@ -6930,6 +6930,7 @@ name = "vectorcraft-ui-egui"
version = "0.24.1"
dependencies = [
"bytemuck",
"chrono",
"craft-keymap",
"craft-ui",
"craft-update",
+6
View File
@@ -432,6 +432,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
),
("view.nextArtboard", "Next Artboard", "", "{} the navigator's Next Artboard button: view.goToArtboard {index: \"next\"} → {index}"),
("view.lastArtboard", "Last Artboard", "", "{} the navigator's Last Artboard button: view.goToArtboard {index: \"last\"} → {index}"),
(
"view.statusBarShow",
"Status Bar Show",
"",
"{show: \"artboardName\" | \"currentTool\" | \"dateTime\" | \"undos\" | \"colorProfile\"} the status bar's Show menu (a click on its info field): the field shows the name of the artboard the navigator shows, the tool in use (the default), the date and time (local on the desktop, UTC on the web), the active document's undo and redo step counts, or the profile its colors use (the one assigned for its color mode, else, or when that one is not available, that mode's working space); kept with the UI state; any other value is an error → show",
),
("view.edges", "Hide Edges", "Cmd+H", "{}"),
("view.artboards", "Hide Artboards", "Cmd+Shift+H", "{}"),
(
+3
View File
@@ -54,6 +54,9 @@ log = { workspace = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
craft-ui = { workspace = true, features = ["native-windows"] }
craft-update = { workspace = true }
# The local date and time the status bar's Date and Time readout shows (`chrono::Local`). Already in
# Cargo.lock with the time-zone crates `clock` uses.
chrono = { workspace = true, features = ["clock"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# `now_ms` (src/lib.rs) reads the page's `performance.now()` with this crate in the web build. It is
+2 -2
View File
@@ -28,9 +28,9 @@ pub(crate) use control_bar::{Fit, group_separator, left_out, link_reserve};
#[cfg(test)]
use hints::hint_segments;
pub use hints::{hint_bar, hint_strings};
pub use status_bar::status_bar;
pub use status_bar::{StatusBarShow, deserialize_status_bar_show, set_status_bar_show, status_bar};
#[cfg(test)]
use status_bar::{StatusReadoutWidths, status_readouts};
use status_bar::{StatusReadoutWidths, color_profile_name, date_time_label, status_readouts};
#[cfg(test)]
use tabs::{CLOSE_BEFORE_TITLE, first_tab_shown, tab_drop, tab_groups, tab_title};
pub use tabs::{doc_tabs, isolation_bar, step_document};
+175 -11
View File
@@ -73,19 +73,37 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
app.status(e);
}
ui.separator();
let tool = vectorcraft_tools::tool_info(app.session.tool_id())
.map(|t| {
// The catalog keys carry the full label ("Selection Tool"); only English drops the suffix.
let shown = tl!(t.label);
if crate::i18n::current() == crate::i18n::Lang::EN { shown.trim_end_matches(" Tool") } else { shown }
})
.unwrap_or("");
// The tool name leaves the status message its least width and ends in an ellipsis first.
let (info, next) = info_text(app);
if let Some(secs) = next {
ui.ctx().request_repaint_after(std::time::Duration::from_secs(secs));
}
// The info field leaves the status message its least width and ends in an ellipsis first.
// An empty readout shows a dash, so the field can still be clicked.
let room = (ui.available_width() - MESSAGE_MIN - ui.spacing().item_spacing.x).max(0.0);
ui.scope(|ui| {
ui.set_max_width(room);
clipped_label(ui, egui::RichText::new(tool).size(11.5).color(t.text_dim));
let shown = if info.is_empty() { "–" } else { info.as_str() };
let field = ui
.scope(|ui| {
ui.set_max_width(room);
clipped_label(ui, egui::RichText::new(shown).size(11.5).color(t.text_dim))
})
.inner
.interact(Sense::click());
let mut pick = None;
egui::Popup::menu(&field).show(|ui| {
ui.menu_button(tl!("Show"), |ui| {
for show in StatusBarShow::ALL {
if widgets::menu_item_name(ui, show.label(), true, app.ui.status_bar_show == show) {
pick = Some(show);
ui.close();
}
}
});
});
if let Some(show) = pick
&& let Err(e) = app.run("view.statusBarShow", json!({ "show": show.param() }))
{
app.status(e);
}
status_readouts(app, ui, &t);
});
});
@@ -95,6 +113,152 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
/// The least width the status message keeps when the window is narrow.
const MESSAGE_MIN: f32 = 180.0;
/// What the status bar's info field shows, chosen from the Show menu that a click on the field
/// opens (`view.statusBarShow`). Kept with the UI state ([`crate::UiState::status_bar_show`]).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub enum StatusBarShow {
/// The name of the artboard the navigator shows.
ArtboardName,
/// The name of the tool in use.
#[default]
CurrentTool,
/// The date and time.
DateTime,
/// How many steps the active document can undo and redo.
Undos,
/// The profile the active document's colors use.
ColorProfile,
}
impl StatusBarShow {
/// Every choice, in the Show menu's order.
pub const ALL: [StatusBarShow; 5] =
[StatusBarShow::ArtboardName, StatusBarShow::CurrentTool, StatusBarShow::DateTime, StatusBarShow::Undos, StatusBarShow::ColorProfile];
/// The `show` param of `view.statusBarShow`.
pub fn param(self) -> &'static str {
match self {
StatusBarShow::ArtboardName => "artboardName",
StatusBarShow::CurrentTool => "currentTool",
StatusBarShow::DateTime => "dateTime",
StatusBarShow::Undos => "undos",
StatusBarShow::ColorProfile => "colorProfile",
}
}
/// The choice whose param is `param`.
pub fn from_param(param: &str) -> Option<StatusBarShow> {
StatusBarShow::ALL.into_iter().find(|s| s.param() == param)
}
/// The Show menu's label, in the interface language.
pub fn label(self) -> &'static str {
match self {
StatusBarShow::ArtboardName => tl!("Artboard Name"),
StatusBarShow::CurrentTool => tl!("Current Tool"),
StatusBarShow::DateTime => tl!("Date and Time"),
StatusBarShow::Undos => tl!("Number of Undos"),
StatusBarShow::ColorProfile => tl!("Document Color Profile"),
}
}
}
/// Serde reader for the saved choice ([`crate::UiState::status_bar_show`]): a value this version
/// does not know reads as Current Tool.
pub fn deserialize_status_bar_show<'de, D: serde::Deserializer<'de>>(d: D) -> Result<StatusBarShow, D::Error> {
let v = <serde_json::Value as serde::Deserialize>::deserialize(d)?;
Ok(v.as_str().and_then(StatusBarShow::from_param).unwrap_or_default())
}
/// `view.statusBarShow {show}`: the info field shows that readout from now on → the choice.
pub fn set_status_bar_show(app: &mut VectorcraftApp, p: &serde_json::Value) -> Result<serde_json::Value, String> {
let key = "show";
let Some(show) = p.get(key).and_then(serde_json::Value::as_str).and_then(StatusBarShow::from_param) else {
let all: Vec<&str> = StatusBarShow::ALL.iter().map(|s| s.param()).collect();
return Err(format!("`{key}` must be one of {}", all.join(", ")));
};
app.ui.status_bar_show = show;
Ok(json!(show.param()))
}
/// The info field's text for the choice in the Show menu, and for Date and Time the seconds until
/// the minute it shows ends (when the bar draws again).
pub(super) fn info_text(app: &VectorcraftApp) -> (String, Option<u64>) {
let st = app.session.active();
match app.ui.status_bar_show {
StatusBarShow::CurrentTool => {
let tool = vectorcraft_tools::tool_info(app.session.tool_id())
.map(|t| {
// The catalog keys carry the full label ("Selection Tool"); only English drops the suffix.
let shown = tl!(t.label);
if crate::i18n::current() == crate::i18n::Lang::EN { shown.trim_end_matches(" Tool") } else { shown }
})
.unwrap_or("");
(tool.to_string(), None)
}
StatusBarShow::ArtboardName => {
let name = st.and_then(|d| {
let n = d.doc.artboards.len();
d.doc.artboards.get(crate::panels::artboards::selected(app, n)).map(|a| a.name.clone())
});
(name.unwrap_or_default(), None)
}
StatusBarShow::DateTime => {
let (text, next) = date_time_now();
(text, Some(next))
}
StatusBarShow::Undos => {
let text = st.map(|d| format!("{}: {} {}: {}", tl!("Undos"), d.history.undo.len(), tl!("Redos"), d.history.redo.len()));
(text.unwrap_or_default(), None)
}
StatusBarShow::ColorProfile => (st.map(|d| color_profile_name(&d.doc)).unwrap_or_default(), None),
}
}
/// The profile `doc`'s colors use for its color mode, as the context it draws through has it
/// ([`vectorcraft_engine::cmd::colormgmt::doc_cms`]): the one assigned (Edit › Assign Profile), else
/// the working space of that mode (Edit › Color Settings), also when the assigned one is not
/// available.
pub(super) fn color_profile_name(doc: &vectorcraft_doc::Document) -> String {
let cms = vectorcraft_engine::cmd::colormgmt::doc_cms(doc);
let settings = cms.settings();
let name = match doc.color_mode {
vectorcraft_doc::ColorMode::Rgb => &settings.rgb,
vectorcraft_doc::ColorMode::Cmyk => &settings.cmyk,
};
vectorcraft_color::cms::canonical_name(name).to_string()
}
/// `[year, month, day, hour, minute, second]` as the info field shows it: `2026-11-15 14:03`.
pub(super) fn date_time_label([y, mo, d, h, mi, _]: [i64; 6]) -> String {
format!("{y:04}-{mo:02}-{d:02} {h:02}:{mi:02}")
}
/// The local date and time ([`date_time_label`]) and the seconds until the minute ends.
#[cfg(not(target_arch = "wasm32"))]
fn date_time_now() -> (String, u64) {
use chrono::{Datelike, Timelike};
let now = chrono::Local::now();
let parts = [
i64::from(now.year()),
i64::from(now.month()),
i64::from(now.day()),
i64::from(now.hour()),
i64::from(now.minute()),
i64::from(now.second()),
];
(date_time_label(parts), 60 - u64::from(now.second().min(59)))
}
/// The web build reads no time zone, so it shows the date and time in UTC.
#[cfg(target_arch = "wasm32")]
fn date_time_now() -> (String, u64) {
let secs = web_time::SystemTime::now().duration_since(web_time::UNIX_EPOCH).map_or(0, |d| d.as_secs());
let parts = vectorcraft_doc::metadata::civil(i64::try_from(secs).unwrap_or(i64::MAX));
(format!("{} UTC", date_time_label(parts)), 60 - u64::try_from(parts[5].clamp(0, 59)).unwrap_or(0))
}
/// A label that ends in an ellipsis when it is wider than the room left in `ui`. Only a cut label
/// shows its full text in a tooltip; screen readers always receive the full text.
pub(super) fn clipped_label(ui: &mut Ui, text: impl Into<egui::WidgetText>) -> egui::Response {
+149
View File
@@ -576,6 +576,155 @@ fn a_clipped_tool_name_shows_in_full_on_hover() {
}
}
/// The texts of one status bar frame after `events`, `dt` seconds after the last one.
fn status_after(app: &mut crate::VectorcraftApp, ctx: &egui::Context, dt: f64, events: Vec<egui::Event>) -> Vec<StatusText> {
let t = ctx.input(|i| i.time) + dt;
bar_frame(app, ctx, 1400.0, Some(t), events, super::status_bar).0
}
/// A press and release of the primary button at `pos`.
fn click_at(pos: egui::Pos2) -> Vec<egui::Event> {
let button = |pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
vec![egui::Event::PointerMoved(pos), button(true), button(false)]
}
/// The center of the first text that is `text`.
fn text_center(texts: &[StatusText], text: &str) -> egui::Pos2 {
let at = texts.iter().find(|t| t.text == text).unwrap_or_else(|| panic!("no {text:?} in {texts:?}"));
at.clip.intersect(at.bounds).center()
}
/// The status bar's info field shows the readout `view.statusBarShow` chooses: the navigator's
/// artboard name, the tool in use, the date and time, the undo and redo counts, or the document's
/// color profile.
#[test]
fn the_status_bar_info_field_shows_the_chosen_readout() {
let (mut app, ctx) = status_readout_app();
let texts = status_after(&mut app, &ctx, 0.1, vec![]);
assert!(texts.iter().any(|t| t.text == "Selection"), "Current Tool by default: {texts:?}");
app.run("artboard.new", json!({"x": 200, "y": 0, "width": 100, "height": 100, "name": "Back Cover"})).unwrap();
app.run("view.statusBarShow", json!({"show": "artboardName"})).unwrap();
let first = app.session.active().unwrap().doc.artboards[0].name.clone();
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == first), "the first artboard's name");
app.run("view.goToArtboard", json!({"index": 1})).unwrap();
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == "Back Cover"), "the navigator's artboard");
app.run("view.statusBarShow", json!({"show": "undos"})).unwrap();
let counts = |app: &crate::VectorcraftApp| {
let h = &app.session.active().unwrap().history;
format!("Undos: {} Redos: {}", h.undo.len(), h.redo.len())
};
let before = app.session.active().unwrap().history.undo.len();
app.session.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap();
assert_eq!(counts(&app), format!("Undos: {} Redos: 0", before + 1));
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == counts(&app)));
app.run("edit.undo", json!({})).unwrap();
assert_eq!(counts(&app), format!("Undos: {before} Redos: 1"));
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == counts(&app)), "the redo step counts");
app.run("view.statusBarShow", json!({"show": "colorProfile"})).unwrap();
let working = vectorcraft_color::cms::working_settings();
let mode_working = |app: &crate::VectorcraftApp| match app.session.active().unwrap().doc.color_mode {
vectorcraft_doc::ColorMode::Rgb => working.rgb.clone(),
vectorcraft_doc::ColorMode::Cmyk => working.cmyk.clone(),
};
let untagged = vectorcraft_color::cms::canonical_name(&mode_working(&app)).to_string();
assert_eq!(super::color_profile_name(&app.session.active().unwrap().doc), untagged, "untagged: the working space");
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == untagged));
// A profile assigned for the document's color mode shows; one that is not available shows the
// working space the document draws through.
let (key, assigned) = match app.session.active().unwrap().doc.color_mode {
vectorcraft_doc::ColorMode::Rgb => ("rgb", vectorcraft_color::cms::PROPHOTO_RGB),
vectorcraft_doc::ColorMode::Cmyk => ("cmyk", vectorcraft_color::cms::DEVICE_CMYK),
};
app.run("edit.assignProfile", json!({ key: assigned })).unwrap();
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == assigned), "the assigned profile");
let st = app.session.active_mut().unwrap();
std::sync::Arc::make_mut(&mut st.doc).color_profiles =
vectorcraft_doc::ColorProfiles { rgb: Some("Not Installed".into()), cmyk: Some("Not Installed".into()) };
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == untagged), "an unavailable profile: the working space");
app.run("view.statusBarShow", json!({"show": "dateTime"})).unwrap();
let texts = status_after(&mut app, &ctx, 0.1, vec![]);
let is_date = |s: &str| {
let b = s.as_bytes();
b.len() == 16
&& b.iter().enumerate().all(|(i, c)| match i {
4 | 7 => *c == b'-',
10 => *c == b' ',
13 => *c == b':',
_ => c.is_ascii_digit(),
})
};
assert!(texts.iter().any(|t| is_date(&t.text)), "a date and time: {texts:?}");
assert_eq!(super::date_time_label([2026, 3, 7, 9, 5, 59]), "2026-03-07 09:05");
// Without a document the field shows a dash, which can still be clicked.
app.run("view.statusBarShow", json!({"show": "undos"})).unwrap();
while app.session.active().is_some() {
app.session.execute("file.close", &json!({})).unwrap();
}
assert!(status_after(&mut app, &ctx, 0.1, vec![]).iter().any(|t| t.text == "–"));
}
/// A click on the info field opens its Show menu, which checks the readout shown and changes it.
#[test]
fn a_click_on_the_info_field_opens_the_show_menu() {
let (mut app, ctx) = status_readout_app();
status_after(&mut app, &ctx, 1.0, vec![egui::Event::PointerGone]);
let texts = status_after(&mut app, &ctx, 0.1, vec![]);
let field = text_center(&texts, "Selection");
status_after(&mut app, &ctx, 0.1, click_at(field));
let texts = status_after(&mut app, &ctx, 0.1, vec![]);
let show = text_center(&texts, "Show");
status_after(&mut app, &ctx, 0.1, vec![egui::Event::PointerMoved(show)]);
status_after(&mut app, &ctx, 0.5, vec![]);
let texts = status_after(&mut app, &ctx, 0.5, vec![]);
for label in [" Artboard Name", "✓ Current Tool", " Date and Time", " Number of Undos", " Document Color Profile"] {
assert!(texts.iter().any(|t| t.text == label), "no {label:?} in {texts:?}");
}
let undos = text_center(&texts, " Number of Undos");
status_after(&mut app, &ctx, 0.1, vec![egui::Event::PointerMoved(egui::pos2(undos.x, show.y)), egui::Event::PointerMoved(undos)]);
status_after(&mut app, &ctx, 0.1, click_at(undos));
assert_eq!(app.ui.status_bar_show, super::StatusBarShow::Undos);
assert_eq!(crate::menus::checked(&app, "view.statusBarShow", &json!({"show": "undos"})), Some(true));
assert_eq!(crate::menus::checked(&app, "view.statusBarShow", &json!({"show": "currentTool"})), Some(false));
}
/// `view.statusBarShow` takes only the five readouts, leaving the choice as it was otherwise, and the
/// choice is saved with the UI state, which reads a value it does not know as Current Tool.
#[test]
fn the_status_bar_show_choice_is_checked_and_saved() {
let (mut app, _ctx) = status_readout_app();
assert_eq!(app.run("view.statusBarShow", json!({"show": "dateTime"})).unwrap(), json!("dateTime"));
for bad in [json!({}), json!({"show": "zoom"}), json!({"show": 2}), json!({"show": null}), json!({"show": "DateTime"})] {
let err = app.run("view.statusBarShow", bad.clone()).unwrap_err();
assert!(err.contains("`show` must be one of artboardName, currentTool, dateTime, undos, colorProfile"), "{bad}: {err}");
assert_eq!(app.ui.status_bar_show, super::StatusBarShow::DateTime, "{bad} leaves the choice");
}
let saved = serde_json::to_value(&app.ui).unwrap();
assert_eq!(saved["status_bar_show"], json!("dateTime"));
let read: crate::state::UiState = serde_json::from_value(saved.clone()).unwrap();
assert_eq!(read.status_bar_show, super::StatusBarShow::DateTime);
for (stored, shown) in [
(json!("colorProfile"), super::StatusBarShow::ColorProfile),
(json!("zoom"), super::StatusBarShow::CurrentTool),
(json!(7), super::StatusBarShow::CurrentTool),
] {
let mut v = saved.clone();
v["status_bar_show"] = stored;
assert_eq!(serde_json::from_value::<crate::state::UiState>(v).unwrap().status_bar_show, shown);
}
let mut v = saved;
v.as_object_mut().unwrap().remove("status_bar_show");
assert_eq!(
serde_json::from_value::<crate::state::UiState>(v).unwrap().status_bar_show,
super::StatusBarShow::CurrentTool,
"state saved before it"
);
}
/// Busy-job transitions preserve message placement, labels and accessibility.
#[test]
fn background_activity_keeps_the_status_message_origin() {
+8
View File
@@ -1348,6 +1348,14 @@ font-technology Variable Променливи
Previous Artboard Предишна работна област
Next Artboard Следваща работна област
Last Artboard Последна работна област
Status Bar Show Показване в лентата на състоянието
Artboard Name Име на работната област
Current Tool Текущ инструмент
Date and Time Дата и час
Number of Undos Брой отмени
Document Color Profile Цветови профил на документа
Undos Отмени
Redos Повторения
Go To Link Към връзката
GPL palettes keep solid colours only, as RGB. Палитрите GPL запазват само плътни цветове като RGB.
GPU Performance Производителност на графичния процесор
Can't render this file because it is too large.
+8
View File
@@ -1085,6 +1085,14 @@ lighting Light Svjetlo
Previous Artboard Prethodna radna ploča
Next Artboard Sljedeća radna ploča
Last Artboard Posljednja radna ploča
Status Bar Show Prikaz u traci stanja
Artboard Name Naziv radne ploče
Current Tool Trenutni alat
Date and Time Datum i vrijeme
Number of Undos Broj poništavanja
Document Color Profile Profil boja dokumenta
Undos Poništavanja
Redos Ponavljanja
Graphic Style Options… Opcije grafičkog stila…
Symbol Options Opcije simbola
Symbol Options… Opcije simbola…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8
View File
@@ -2325,6 +2325,14 @@ digits Malayalam Malaiàlam
Enclosing Lasso Llaç envoltant
Last Últim
Last Artboard Últim tauler
Status Bar Show Mostra a la barra d'estat
Artboard Name Nom del tauler
Current Tool Eina actual
Date and Time Data i hora
Number of Undos Nombre de desfets
Document Color Profile Perfil de color del document
Undos Desfets
Redos Refets
Last commit Últim commit
Last Effect: {name} Últim efecte: {name}
Last Effect: {} Últim efecte: {}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8
View File
@@ -1109,6 +1109,14 @@ lighting Light Světlo
Previous Artboard Předchozí pracovní plocha
Next Artboard Další pracovní plocha
Last Artboard Poslední pracovní plocha
Status Bar Show Zobrazit ve stavovém řádku
Artboard Name Název pracovní plochy
Current Tool Aktuální nástroj
Date and Time Datum a čas
Number of Undos Počet kroků zpět
Document Color Profile Barevný profil dokumentu
Undos Zpět
Redos Znovu
Graphic Style Options… Možnosti grafického stylu…
Symbol Options Možnosti symbolu
Symbol Options… Možnosti symbolu…
Can't render this file because it is too large.
+8
View File
@@ -6527,6 +6527,14 @@ swatch Bright Violet Kraftig violet
Color wheel Farvehjul
First Artboard Første kunstplade
Last Artboard Sidste kunstplade
Status Bar Show Vis i statuslinjen
Artboard Name Kunstpladens navn
Current Tool Aktuelt værktøj
Date and Time Dato og klokkeslæt
Number of Undos Antal fortrydelser
Document Color Profile Dokumentets farveprofil
Undos Fortrydelser
Redos Gentagelser
Next Artboard Næste kunstplade
Previous Artboard Forrige kunstplade
Step up or down Trinvis op eller ned
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8
View File
@@ -1393,6 +1393,14 @@ file-filter Templates Vorlagen
Previous Artboard Vorherige Zeichenfläche
Next Artboard Nächste Zeichenfläche
Last Artboard Letzte Zeichenfläche
Status Bar Show In Statusleiste anzeigen
Artboard Name Name der Zeichenfläche
Current Tool Aktuelles Werkzeug
Date and Time Datum und Uhrzeit
Number of Undos Anzahl der Rückgängig-Schritte
Document Color Profile Farbprofil des Dokuments
Undos Rückgängig
Redos Wiederholen
Go To Link Gehe zu Verknüpfung
GPL palettes keep solid colours only, as RGB. GPL-Paletten enthalten nur einfarbige Farben, als RGB.
GPU Performance GPU-Leistung
Can't render this file because it is too large.
+8
View File
@@ -1323,6 +1323,14 @@ font-technology Variable Μεταβλητές
Previous Artboard Προηγούμενος πίνακας σχεδίασης
Next Artboard Επόμενος πίνακας σχεδίασης
Last Artboard Τελευταίος πίνακας σχεδίασης
Status Bar Show Εμφάνιση στη γραμμή κατάστασης
Artboard Name Όνομα πίνακα σχεδίασης
Current Tool Τρέχον εργαλείο
Date and Time Ημερομηνία και ώρα
Number of Undos Αριθμός αναιρέσεων
Document Color Profile Χρωματικό προφίλ εγγράφου
Undos Αναιρέσεις
Redos Επαναλήψεις
Go To Link Μετάβαση στη σύνδεση
GPL palettes keep solid colours only, as RGB. Οι παλέτες GPL διατηρούν μόνο συμπαγή χρώματα σε RGB.
GPU Performance Απόδοση GPU
Can't render this file because it is too large.
+8
View File
@@ -1402,6 +1402,14 @@ font-technology Variable Variables
Previous Artboard Mesa de trabajo anterior
Next Artboard Mesa de trabajo siguiente
Last Artboard Última mesa de trabajo
Status Bar Show Mostrar en la barra de estado
Artboard Name Nombre de la mesa de trabajo
Current Tool Herramienta actual
Date and Time Fecha y hora
Number of Undos Número de deshacer
Document Color Profile Perfil de color del documento
Undos Deshacer
Redos Rehacer
Go To Link Ir al vínculo
GPL palettes keep solid colours only, as RGB. Las paletas GPL solo conservan colores sólidos, en RGB.
GPU Performance Rendimiento de GPU
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8
View File
@@ -1088,6 +1088,14 @@ lighting Light Valo
Previous Artboard Edellinen piirtoalusta
Next Artboard Seuraava piirtoalusta
Last Artboard Viimeinen piirtoalusta
Status Bar Show Näytä tilarivillä
Artboard Name Piirtoalustan nimi
Current Tool Nykyinen työkalu
Date and Time Päivämäärä ja aika
Number of Undos Kumousten määrä
Document Color Profile Asiakirjan väriprofiili
Undos Kumoukset
Redos Uudelleentekot
Graphic Style Options… Grafiikkatyylin asetukset…
Symbol Options Symbolin asetukset
Symbol Options… Symbolin asetukset…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+8
View File
@@ -1396,6 +1396,14 @@ font-technology Variable Variables
Previous Artboard Plan de travail précédent
Next Artboard Plan de travail suivant
Last Artboard Dernier plan de travail
Status Bar Show Afficher dans la barre d'état
Artboard Name Nom du plan de travail
Current Tool Outil actif
Date and Time Date et heure
Number of Undos Nombre d'annulations
Document Color Profile Profil colorimétrique du document
Undos Annulations
Redos Rétablissements
Go To Link Atteindre le lien
GPL palettes keep solid colours only, as RGB. Les palettes GPL ne conservent que les couleurs unies, en RVB.
GPU Performance Performances GPU
Can't render this file because it is too large.
+8
View File
@@ -1908,6 +1908,14 @@
Lasso Tool લાસો ટૂલ
Last છેલ્લું
Last Artboard છેલ્લું આર્ટબોર્ડ
Status Bar Show સ્ટેટસ બારમાં બતાવો
Artboard Name આર્ટબોર્ડનું નામ
Current Tool વર્તમાન ટૂલ
Date and Time તારીખ અને સમય
Number of Undos પૂર્વવત્ કરવાની સંખ્યા
Document Color Profile દસ્તાવેજની રંગ પ્રોફાઇલ
Undos પૂર્વવત્
Redos ફરીથી કરો
Last Effect: {name} છેલ્લી ઇફેક્ટ: {name}
Last Effect… છેલ્લી ઇફેક્ટ…
Last Page છેલ્લું પેજ
Can't render this file because it contains an unexpected character in line 72 and column 5.
+8
View File
@@ -2570,6 +2570,14 @@ antiAlias Sharp शार्प
Lasso Tool लैस्सो टूल
Last अंतिम
Last Artboard अंतिम आर्टबोर्ड
Status Bar Show स्टेटस बार में दिखाएँ
Artboard Name आर्टबोर्ड का नाम
Current Tool मौजूदा टूल
Date and Time तारीख और समय
Number of Undos पूर्ववत करने की संख्या
Document Color Profile दस्तावेज़ की रंग प्रोफ़ाइल
Undos पूर्ववत
Redos फिर से करें
Last commit अंतिम कमिट
Last Effect: {} अंतिम इफ़ेक्ट: {}
Last Effect: {name} अंतिम इफ़ेक्ट: {name}
Can't render this file because it is too large.
+8
View File
@@ -1744,6 +1744,14 @@ file-filter Templates Sablonok
Previous Artboard Előző rajztábla
Next Artboard Következő rajztábla
Last Artboard Utolsó rajztábla
Status Bar Show Megjelenítés az állapotsorban
Artboard Name Rajztábla neve
Current Tool Aktuális eszköz
Date and Time Dátum és idő
Number of Undos Visszavonások száma
Document Color Profile Dokumentum színprofilja
Undos Visszavonások
Redos Újra
Go to Conflict Ugrás az ütközésre
Go To Link Ugrás a csatoláshoz
Good Jó
Can't render this file because it is too large.
+8
View File
@@ -5858,6 +5858,14 @@ swatch Bright Violet Violet cerah
Color wheel Roda warna
First Artboard Artboard pertama
Last Artboard Artboard terakhir
Status Bar Show Tampilkan di bilah status
Artboard Name Nama artboard
Current Tool Alat saat ini
Date and Time Tanggal dan waktu
Number of Undos Jumlah urungkan
Document Color Profile Profil warna dokumen
Undos Urungkan
Redos Ulangi
Next Artboard Artboard berikutnya
Previous Artboard Artboard sebelumnya
Step up or down Naikkan atau turunkan bertahap
Can't render this file because it contains an unexpected character in line 2467 and column 5.
+8
View File
@@ -1423,6 +1423,14 @@ file-filter Templates Modelli
Previous Artboard Tavola da disegno precedente
Next Artboard Tavola da disegno successiva
Last Artboard Ultima tavola da disegno
Status Bar Show Mostra nella barra di stato
Artboard Name Nome tavola da disegno
Current Tool Strumento attivo
Date and Time Data e ora
Number of Undos Numero di annullamenti
Document Color Profile Profilo colore del documento
Undos Annullamenti
Redos Ripristini
Go To Link Vai al collegamento
GPL palettes keep solid colours only, as RGB. Le palette GPL conservano solo i colori pieni, in RGB.
GPU Performance Prestazioni GPU
Can't render this file because it is too large.
+8
View File
@@ -1885,6 +1885,14 @@ antiAlias Sharp シャープ
Previous Artboard 前のアートボード
Next Artboard 次のアートボード
Last Artboard 最後のアートボード
Status Bar Show ステータスバーの表示
Artboard Name アートボード名
Current Tool 現在のツール
Date and Time 日付と時刻
Number of Undos 取り消し回数
Document Color Profile ドキュメントのカラープロファイル
Undos 取り消し
Redos やり直し
Gradient Fill: click to apply, drag onto art グラデーションの塗り: クリックで適用、アートにドラッグ
Gradient Mesh グラデーションメッシュ
Gradient Tool グラデーションツール
Can't render this file because it is too large.
+8
View File
@@ -6146,6 +6146,14 @@ imageTrace Outline კონტური
JavaScript: JavaScript:
Large Gaps დიდი ღრეჩოები
Last Artboard ბოლო სამუშაო დაფა
Status Bar Show ჩვენება მდგომარეობის ზოლში
Artboard Name სამუშაო დაფის სახელი
Current Tool მიმდინარე ხელსაწყო
Date and Time თარიღი და დრო
Number of Undos დაბრუნებების რაოდენობა
Document Color Profile დოკუმენტის ფერის პროფილი
Undos დაბრუნებები
Redos ხელახლა შესრულებები
Levels დონეები
Library Panel Options ბიბლიოთეკის პანელის პარამეტრები
Locate Library… ბიბლიოთეკის მოძებნა…
Can't render this file because it is too large.
+8
View File
@@ -1875,6 +1875,14 @@ antiAlias Sharp 날카롭게
Previous Artboard 이전 아트보드
Next Artboard 다음 아트보드
Last Artboard 마지막 아트보드
Status Bar Show 상태 표시줄 표시
Artboard Name 아트보드 이름
Current Tool 현재 도구
Date and Time 날짜 및 시간
Number of Undos 실행 취소 횟수
Document Color Profile 문서 색상 프로파일
Undos 실행 취소
Redos 다시 실행
Gradient Fill: click to apply, drag onto art 그레이디언트 칠: 클릭하여 적용, 아트웍에 끌어 놓기
Gradient Mesh 그레이디언트 메시
Gradient Tool 그레이디언트 도구
Can't render this file because it is too large.
+8
View File
@@ -8307,6 +8307,14 @@ swatch Bright Violet Felviolet
Color wheel Kleurenwiel
First Artboard Eerste tekenbord
Last Artboard Laatste tekenbord
Status Bar Show Weergeven in statusbalk
Artboard Name Naam van tekenbord
Current Tool Huidig gereedschap
Date and Time Datum en tijd
Number of Undos Aantal ongedaan gemaakte stappen
Document Color Profile Kleurprofiel van document
Undos Ongedaan maken
Redos Opnieuw uitvoeren
Next Artboard Volgende tekenbord
Previous Artboard Vorige tekenbord
Step up or down Stapsgewijs verhogen of verlagen
Can't render this file because it is too large.
+8
View File
@@ -1045,6 +1045,14 @@ lighting Light Światło
Previous Artboard Poprzedni obszar kompozycji
Next Artboard Następny obszar kompozycji
Last Artboard Ostatni obszar kompozycji
Status Bar Show Pokaż na pasku stanu
Artboard Name Nazwa obszaru kompozycji
Current Tool Bieżące narzędzie
Date and Time Data i godzina
Number of Undos Liczba cofnięć
Document Color Profile Profil kolorów dokumentu
Undos Cofnięcia
Redos Ponowienia
Graphic Style Options… Opcje stylu graficznego…
Symbol Options Opcje symbolu
Symbol Options… Opcje symbolu…
Can't render this file because it is too large.
+8
View File
@@ -1258,6 +1258,14 @@ antiAlias Sharp Nítido
Previous Artboard Mesa de trabalho anterior
Next Artboard Próxima mesa de trabalho
Last Artboard Última mesa de trabalho
Status Bar Show Mostrar na barra de status
Artboard Name Nome da prancheta
Current Tool Ferramenta atual
Date and Time Data e hora
Number of Undos Número de ações desfeitas
Document Color Profile Perfil de cor do documento
Undos Desfazer
Redos Refazer
Gradient Gradiente
Gradient Fill: click to apply, drag onto art Preenchimento de Gradiente: clique para aplicar, arraste sobre a arte
Gradient Finishes Finalizações de Gradiente
Can't render this file because it is too large.
+8
View File
@@ -1412,6 +1412,14 @@ file-filter Templates Шаблоны
Previous Artboard Предыдущая монтажная область
Next Artboard Следующая монтажная область
Last Artboard Последняя монтажная область
Status Bar Show Показ в строке состояния
Artboard Name Имя монтажной области
Current Tool Текущий инструмент
Date and Time Дата и время
Number of Undos Число отмен
Document Color Profile Цветовой профиль документа
Undos Отмены
Redos Повторы
Go To Link Перейти по ссылке
GPL palettes keep solid colours only, as RGB. Палитры GPL сохраняют только сплошные цвета в RGB.
GPU Performance Производительность GPU
Can't render this file because it is too large.
+8
View File
@@ -1361,6 +1361,14 @@ font-technology Variable Variabla
Previous Artboard Föregående rityta
Next Artboard Nästa rityta
Last Artboard Sista ritytan
Status Bar Show Visa i statusfältet
Artboard Name Ritytans namn
Current Tool Aktuellt verktyg
Date and Time Datum och tid
Number of Undos Antal ångringar
Document Color Profile Dokumentets färgprofil
Undos Ångringar
Redos Gör om
Go To Link Gå till länk
GPL palettes keep solid colours only, as RGB. GPL-paletter sparar bara enfärgade färger, som RGB.
GPU Performance GPU-prestanda
Can't render this file because it is too large.
+8
View File
@@ -6135,6 +6135,14 @@ theme Appearance Görünüm
JavaScript: JavaScript:
Large Gaps Büyük Boşluklar
Last Artboard Son Çalışma Yüzeyi
Status Bar Show Durum Çubuğunda Göster
Artboard Name Çalışma Yüzeyi Adı
Current Tool Geçerli Araç
Date and Time Tarih ve Saat
Number of Undos Geri Alma Sayısı
Document Color Profile Belge Renk Profili
Undos Geri Al
Redos Yinele
Levels Düzeyler
Library Panel Options Kütüphane Paneli Seçenekleri
Link JavaScript Files… JavaScript Dosyalarını Bağla…
Can't render this file because it is too large.
+8
View File
@@ -1417,6 +1417,14 @@ file-filter Templates Шаблони
Previous Artboard Попередня монтажна область
Next Artboard Наступна монтажна область
Last Artboard Остання монтажна область
Status Bar Show Показ у рядку стану
Artboard Name Назва монтажної області
Current Tool Поточний інструмент
Date and Time Дата й час
Number of Undos Кількість скасувань
Document Color Profile Колірний профіль документа
Undos Скасування
Redos Повтори
Go To Link Перейти за посиланням
GPL palettes keep solid colours only, as RGB. Палітри GPL зберігають лише суцільні кольори RGB.
GPU Performance Продуктивність GPU
Can't render this file because it is too large.
+8
View File
@@ -1653,6 +1653,14 @@ file-filter Templates Mẫu
Previous Artboard Bảng vẽ trước
Next Artboard Bảng vẽ tiếp theo
Last Artboard Bảng vẽ cuối cùng
Status Bar Show Hiển thị trên thanh trạng thái
Artboard Name Tên bảng vẽ
Current Tool Công cụ hiện tại
Date and Time Ngày và giờ
Number of Undos Số lần hoàn tác
Document Color Profile Hồ sơ màu của tài liệu
Undos Hoàn tác
Redos Làm lại
Go To Link Đến liên kết
Good Tốt
GPL palettes keep solid colours only, as RGB. Bảng màu GPL chỉ lưu màu đặc theo RGB.
Can't render this file because it contains an unexpected character in line 205 and column 5.
+8
View File
@@ -1352,6 +1352,14 @@ file-filter Templates 模板
Previous Artboard 上一个画板
Next Artboard 下一个画板
Last Artboard 最后一个画板
Status Bar Show 状态栏显示
Artboard Name 画板名称
Current Tool 当前工具
Date and Time 日期和时间
Number of Undos 撤销次数
Document Color Profile 文档颜色配置文件
Undos 撤销
Redos 重做
Go To Link 转到链接
GPL palettes keep solid colours only, as RGB. GPL 调板仅保留为 RGB 的纯色。
GPU Performance GPU 性能
Can't render this file because it is too large.
+8
View File
@@ -1387,6 +1387,14 @@ file-filter Templates 範本
Previous Artboard 上一個工作區域
Next Artboard 下一個工作區域
Last Artboard 最後一個工作區域
Status Bar Show 狀態列顯示
Artboard Name 工作區域名稱
Current Tool 目前工具
Date and Time 日期和時間
Number of Undos 復原次數
Document Color Profile 文件色彩描述檔
Undos 復原
Redos 重做
Go To Link 前往連結
GPL palettes keep solid colours only, as RGB. GPL 調色盤僅保留純色,並以 RGB 儲存。
GPU Performance GPU 效能
Can't render this file because it is too large.
+1
View File
@@ -455,6 +455,7 @@ fn run_ui_part5(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
Ok(Value::Null)
}
"view.goToArtboard" => go_to_artboard(app, p),
"view.statusBarShow" => crate::chrome::set_status_bar_show(app, p),
// The navigator's four buttons as commands of their own, so Edit › Keyboard Shortcuts lists
// them (view.goToArtboard needs an index, so a key alone can't run it).
"view.firstArtboard" => go_to_artboard(app, &json!({ "index": "first" })),
+1
View File
@@ -63,6 +63,7 @@ pub fn checked(app: &VectorcraftApp, id: &str, p: &Value) -> Option<bool> {
"view.rotate" => app.view().zip(p.get("angle").and_then(Value::as_f64)).is_some_and(|(view, a)| (view.rotation - a).abs() < 1e-9),
"window.control" => app.ui.control_bar,
"window.helpBar" => app.ui.help_bar,
"view.statusBarShow" => p.get("show").and_then(Value::as_str) == Some(app.ui.status_bar_show.param()),
"window.control.dock" => p.get("edge").and_then(Value::as_str) == Some(crate::chrome::control_bar_placement(&app.ui)),
"window.control.options" => p
.get("group")
+6
View File
@@ -433,6 +433,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
),
("view.nextArtboard", "Next Artboard", "", "{} the navigator's Next Artboard button: view.goToArtboard {index: \"next\"} → {index}"),
("view.lastArtboard", "Last Artboard", "", "{} the navigator's Last Artboard button: view.goToArtboard {index: \"last\"} → {index}"),
(
"view.statusBarShow",
"Status Bar Show",
"",
"{show: \"artboardName\" | \"currentTool\" | \"dateTime\" | \"undos\" | \"colorProfile\"} the status bar's Show menu (a click on its info field): the field shows the name of the artboard the navigator shows, the tool in use (the default), the date and time (local on the desktop, UTC on the web), the active document's undo and redo step counts, or the profile its colors use (the one assigned for its color mode, else, or when that one is not available, that mode's working space); kept with the UI state; any other value is an error → show",
),
("view.edges", "Hide Edges", "Cmd+H", "{}"),
("view.artboards", "Hide Artboards", "Cmd+Shift+H", "{}"),
(
+5
View File
@@ -550,6 +550,10 @@ pub struct UiState {
#[serde(default)]
pub toolbar_right: bool,
pub status_bar: bool,
/// What the status bar's info field shows (its Show menu, `view.statusBarShow`): Current Tool by
/// default and in state saved without it ([`crate::chrome::deserialize_status_bar_show`]).
#[serde(default, deserialize_with = "crate::chrome::deserialize_status_bar_show")]
pub status_bar_show: crate::chrome::StatusBarShow,
pub help_bar: bool,
pub dock: bool,
/// The bars that Tab (`window.dock`) hid, which the next Tab shows again; none when Tab has not
@@ -1177,6 +1181,7 @@ impl Default for UiState {
tools_beside_canvas: false,
toolbar_right: false,
status_bar: true,
status_bar_show: crate::chrome::StatusBarShow::CurrentTool,
help_bar: true,
dock: true,
tab_hid: None,
+16
View File
@@ -6623,6 +6623,22 @@ Reset Rotate View is the command `view.rotateReset`:
the angle the selected objects share (`selectionRotation` in `document.inspect`, 0 when their angles differ), so their
bounding box is square to the window. It returns `{angle}`, and with nothing selected it returns "nothing selected".
`view.statusBarShow` (desktop app, the Show menu that a click on the status bar's info field opens) chooses what that
field shows, with `show` set to one of these values:
- `artboardName`: the name of the artboard the status bar's navigator shows.
- `currentTool`: the name of the tool in use. This is the default.
- `dateTime`: the date and time, as `2026-11-15 14:03`. The desktop app shows local time, and the web build shows UTC
with ` UTC` after it.
- `undos`: the active document's undo and redo step counts, as `Undos: 3 Redos: 1`.
- `colorProfile`: the profile the active document's colors use. That is the profile assigned for its color mode
(Edit › Assign Profile), or that mode's working space (Edit › Color Settings) when none is assigned or the
assigned one is not available.
It returns the value set. The choice is kept with the UI state. A missing or other `show` fails with
``` `show` must be one of artboardName, currentTool, dateTime, undos, colorProfile ``` and leaves the choice as it was.
Without a document, the readouts that need one show a dash.
The Smart Guides preferences (Preferences › Smart Guides) apply to `pointer_gesture` with Smart Guides on (the
default view) and to the mouse; they change what the tools show and how far a target pulls, and only Construction
Guides change where a snapped point lands:
+1
View File
@@ -456,6 +456,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Workspaces (save, reset, switch); each remembers the layout changes made in it across switches and restarts until Reset <name> | done | `UiState::workspace_changes` |
| Opening a file: after 0.4 s a progress window with the file name, a moving bar (reading counts no steps), how many more files wait, Cancel (Esc: the file doesn't open) and Continue in Background; never over another dialog or progress window | done | #5930; `opening::show`, `file.cancelOpen`, `file.openInBackground`. Save, Place and Close still show only the status bar's spinner (Place and Save run off the UI thread; Close is immediate) |
| Window › Help Bar shows or hides the tool-help strip apart from the status bar; kept with the preferences and in saved workspaces; Presentation Mode hides both bars | done | `window.helpBar`; `help_bar_toggle_is_independent_and_saved_in_workspaces`, `help_bar_and_status_bar_render_independently`, `legacy_help_bar_visibility_follows_each_layouts_status_bar` |
| Status bar: a click on the info field opens a Show menu that chooses its readout: Artboard Name, Current Tool (the default), Date and Time, Number of Undos (undo and redo counts) or Document Color Profile; kept with the UI state | done | `view.statusBarShow`; [public behavior reference](https://web.archive.org/web/2014/https://helpx.adobe.com/illustrator/using/workspace-basics.html); `the_status_bar_info_field_shows_the_chosen_readout`, `a_click_on_the_info_field_opens_the_show_menu`, `the_status_bar_show_choice_is_checked_and_saved`. The menu labels are VectorCraft's own |
| Background Export: a progress dialog with the file name, the artboard count and a bar (busy for one artboard and for formats made in one go), Cancel (Esc) and Continue in Background; it opens after 0.4 s and never over another dialog. A cancelled export writes no file | done | #2564; on the shared `vectorcraft_doc::cancel` token (#2546), so a cancelled render keeps no half-drawn pattern tile or effect raster. The bar moves between artboards, not within one. Save As, Export for Screens and Export Selection don't show it yet |
| Swatch, graphic style and symbol library panels: each library opens in a panel of its own, several at once, that floats, groups, docks and closes like any panel; opening an open library brings its panel forward; Previous / Next Library change the library in place; the panel menu's Persistent reopens the panel at startup with its view and find field (a library loaded from a file is read again; when its file is missing the panel reopens showing the file's path with Locate Library…, and the status bar names the file) | done | #2603; panel ids `library:<kind>:<id>`, `ui.libraryPanel {panel, library?, view?, showFind?, persistent?}`, `ui.libraryPanel.locate {panel, path?}`; the library panels have no sort option, as before |
| DXF export/import preserves intended curve closure | done | #1299; R13–2018 closed SPLINE bit set/read; an open curve ending at its start stays open; knots and control points unchanged; circular cubic paths export as native CIRCLE/ARC where stroke semantics permit; DXF Options' Preset menu and `document.exportDxf`'s `dxfPreset: "cnc"` set the CNC / CAM options (#5500) |