diff --git a/crates/ui-egui/src/dialogs/effect_gallery.rs b/crates/ui-egui/src/dialogs/effect_gallery.rs new file mode 100644 index 000000000..c2bfd76b4 --- /dev/null +++ b/crates/ui-egui/src/dialogs/effect_gallery.rs @@ -0,0 +1,478 @@ +//! Effect → Effect Gallery: browse Photoshop-style raster filters by category, build a stack, and +//! apply it as separate appearance effects in one undo step. + +use egui; +use serde_json::{Map, Value, json}; +use vectorcraft_effects::{EffectInfo, PIXEL_EFFECTS}; + +use super::{DialogSpec, effect, form}; +use crate::state::Dialog; +use crate::theme::Tokens; +use crate::{VectorcraftApp, widgets}; + +/// Dialog kind for the Effect Gallery. +pub const KIND: &str = "effectGallery"; + +/// Raster effect submenus in the gallery (the six Photoshop-style folders it holds). +const CATEGORIES: [(&str, &[&str]); 6] = [ + ("Artistic", &["Effect", "Artistic"]), + ("Brush Strokes", &["Effect", "Brush Strokes"]), + ("Distort", &["Effect", "Distort"]), + ("Sketch", &["Effect", "Sketch"]), + ("Stylize", &["Effect", "Stylize"]), + ("Texture", &["Effect", "Texture"]), +]; + +pub(super) const SPEC: DialogSpec = DialogSpec { + heading: |_| tl!("Effect Gallery").into(), + body, + confirm, + preview: true, + min_width: 720.0, + max_width: Some(820.0), + ..DialogSpec::FORM +}; + +#[derive(Clone)] +struct StackEntry { + effect: String, + visible: bool, + params: Map, +} + +/// Open the gallery on the current selection. +pub fn open(app: &mut VectorcraftApp, _p: &Value) -> Result { + if !app.session.active().is_some_and(|s| !s.selection.is_empty()) { + return Err(tl!("Select artwork to apply effects").into()); + } + let selected = effects_in_category(0).first().map(|e| e.id).unwrap_or(""); + app.ui.dialog = Some(Dialog::new( + KIND, + json!({ "category": 0, "selected": selected, "stack": [], "stackSelected": 0, "stackFocus": false, "preview": true }), + )); + Ok(Value::Null) +} + +fn effects_in_category(cat: usize) -> Vec { + let path = CATEGORIES.get(cat).map(|(_, p)| *p).unwrap_or(CATEGORIES[0].1); + let mut out: Vec = + vectorcraft_effects::effect_catalog().into_iter().filter(|e| e.menu == path && PIXEL_EFFECTS.contains(&e.id)).collect(); + out.sort_by(|a, b| a.label.cmp(b.label)); + out +} + +fn default_params(id: &str) -> Map { + match vectorcraft_effects::effect_info(id).map(|e| e.defaults) { + Some(Value::Object(m)) => m, + _ => Map::new(), + } +} + +fn merged_params(id: &str, stored: &Map) -> Map { + match vectorcraft_effects::merged_params(id, &Value::Object(stored.clone())) { + Value::Object(m) => m, + _ => default_params(id), + } +} + +fn stack_of(d: &Dialog) -> Vec { + d.fields + .get("stack") + .and_then(Value::as_array) + .map(|rows| { + rows.iter() + .filter_map(|row| { + let effect = row.get("effect").and_then(Value::as_str)?; + let visible = row.get("visible").and_then(Value::as_bool).unwrap_or(true); + let params = row + .get("params") + .and_then(Value::as_object) + .cloned() + .map(|m| merged_params(effect, &m)) + .unwrap_or_else(|| default_params(effect)); + Some(StackEntry { effect: effect.to_string(), visible, params }) + }) + .collect() + }) + .unwrap_or_default() +} + +fn write_stack(d: &mut Dialog, stack: &[StackEntry]) { + let rows: Vec = stack.iter().map(|e| json!({ "effect": e.effect, "visible": e.visible, "params": e.params })).collect(); + d.fields.insert("stack".into(), Value::Array(rows)); +} + +fn category_ix(d: &Dialog) -> usize { + d.fields.get("category").and_then(Value::as_u64).unwrap_or(0) as usize +} + +fn selected_id(d: &Dialog) -> String { + d.str("selected") +} + +fn set_selected(d: &mut Dialog, id: &str) { + d.fields.insert("selected".into(), json!(id)); +} + +fn stack_focus(d: &Dialog) -> bool { + d.fields.get("stackFocus").and_then(Value::as_bool).unwrap_or(false) +} + +fn set_stack_focus(d: &mut Dialog, on: bool) { + d.fields.insert("stackFocus".into(), json!(on)); +} + +fn browse_params(d: &Dialog) -> Map { + let effect = d.fields.get("browseEffect").and_then(Value::as_str).unwrap_or(""); + let stored = d.fields.get("browseParams").and_then(Value::as_object).cloned().unwrap_or_default(); + if effect.is_empty() { stored } else { merged_params(effect, &stored) } +} + +fn set_browse_params(d: &mut Dialog, effect: &str, params: Map) { + d.fields.insert("browseEffect".into(), json!(effect)); + d.fields.insert("browseParams".into(), Value::Object(params)); +} + +/// The effect whose parameters the panel edits, and its values. +fn param_target(d: &Dialog, stack: &[StackEntry], stack_sel: usize, browse: &str) -> (String, Map) { + if stack_focus(d) { + let entry = stack.get(stack_sel); + if let Some(e) = entry { + return (e.effect.clone(), e.params.clone()); + } + } + let id = browse.to_string(); + let params = if d.fields.get("browseEffect").and_then(Value::as_str) == Some(browse) { browse_params(d) } else { default_params(browse) }; + (id, params) +} + +fn write_param_target(d: &mut Dialog, stack: &mut [StackEntry], stack_sel: usize, browse: &str, effect: &str, params: Map) { + if stack_focus(d) { + if let Some(e) = stack.get_mut(stack_sel) + && e.effect == effect + { + e.params = params; + } + return; + } + if browse == effect { + set_browse_params(d, effect, params); + } +} + +/// Reuse the live effect dialog's fields for `effect_id` and `params`. +fn effect_param_fields(app: &mut VectorcraftApp, ui: &mut egui::Ui, effect_id: &str, params: &mut Map) { + if effect_id.is_empty() { + return; + } + let label = + vectorcraft_effects::effect_info(effect_id).map(|e| e.label.trim_end_matches('…').to_string()).unwrap_or_else(|| effect_id.to_string()); + let mut fields = params.clone(); + fields.insert("__effect".into(), json!(effect_id)); + fields.insert("__label".into(), json!(label)); + fields.insert("preview".into(), json!(false)); + let mut temp = Dialog::new("effect", Value::Object(fields)); + (effect::SPEC.body)(app, ui, &mut temp); + if let Value::Object(m) = form::params(&temp) { + *params = m; + } + if let Value::Object(m) = vectorcraft_effects::upgrade_params(effect_id, &Value::Object(params.clone())) { + *params = m; + } +} + +fn stack_batch_commands(stack: &[StackEntry]) -> Vec { + let mut commands = Vec::with_capacity(stack.len() * 2); + for (i, entry) in stack.iter().enumerate() { + let params = Value::Object(entry.params.clone()); + commands.push(json!({ "command": "effect.apply", "params": { "effect": entry.effect, "params": params } })); + if !entry.visible { + commands.push(json!({ "command": "effect.setParams", "params": { "index": i, "visible": false } })); + } + } + commands +} + +fn stack_batch_params(stack: &[StackEntry]) -> Value { + json!({ "label": "Effect Gallery", "commands": stack_batch_commands(stack) }) +} + +/// Live canvas preview of the stack (`command.batch` would settle the dialog interaction each frame). +fn preview_stack_on_canvas(app: &mut VectorcraftApp, stack: &[StackEntry]) { + if app.session.in_interaction() { + let _ = app.session.cancel_interaction(); + } + let _ = app.session.begin_interaction("Effect Gallery"); + for (i, entry) in stack.iter().enumerate() { + let params = Value::Object(entry.params.clone()); + if app.run("effect.apply", json!({ "effect": entry.effect, "params": params })).is_err() { + let _ = app.session.cancel_interaction(); + return; + } + if !entry.visible && app.run("effect.setParams", json!({ "index": i, "visible": false })).is_err() { + let _ = app.session.cancel_interaction(); + return; + } + } +} + +fn stack_preview_row(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, stack: &[StackEntry]) { + const LAST: &str = super::form::PREVIEWED; + ui.add_space(8.0); + let mut on = d.bool("preview"); + if widgets::check(ui, tl!("Preview"), on, true) { + on = !on; + d.fields.insert("preview".into(), json!(on)); + } + if !on { + if d.fields.remove(LAST).is_some() { + let _ = app.session.cancel_interaction(); + } + return; + } + if stack.is_empty() { + if d.fields.remove(LAST).is_some() { + let _ = app.session.cancel_interaction(); + } + return; + } + let batch = stack_batch_params(stack); + if d.fields.get(LAST) != Some(&batch) || !app.session.in_interaction() { + preview_stack_on_canvas(app, stack); + d.fields.insert(LAST.into(), batch); + } +} + +fn effect_label(id: &str) -> String { + vectorcraft_effects::effect_info(id).map(|e| tl!(e.label).trim_end_matches('…').to_string()).unwrap_or_else(|| id.to_string()) +} + +fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { + let t = Tokens::get(ui.ctx()); + let cat = category_ix(d); + let effects = effects_in_category(cat); + let mut sel = selected_id(d); + if !effects.iter().any(|e| e.id == sel) { + sel = effects.first().map(|e| e.id.to_string()).unwrap_or_default(); + set_selected(d, &sel); + } + + let mut stack = stack_of(d); + let mut stack_sel = d.fields.get("stackSelected").and_then(Value::as_u64).unwrap_or(0) as usize; + if stack_sel >= stack.len() && !stack.is_empty() { + stack_sel = stack.len() - 1; + } + let (param_effect, mut param_values) = param_target(d, &stack, stack_sel, &sel); + if !param_effect.is_empty() { + widgets::subheader(ui, tl!("Settings")); + egui::ScrollArea::vertical().max_height(200.0).show(ui, |ui| { + effect_param_fields(app, ui, ¶m_effect, &mut param_values); + }); + write_param_target(d, &mut stack, stack_sel, &sel, ¶m_effect, param_values); + } + + ui.horizontal_top(|ui| { + ui.vertical(|ui| { + widgets::subheader(ui, tl!("Categories")); + ui.set_width(120.0); + for (i, (name, _)) in CATEGORIES.iter().enumerate() { + let count = effects_in_category(i).len(); + if count == 0 { + continue; + } + let label = tl!(name); + if ui.add(egui::Button::selectable(cat == i, label)).clicked() { + d.fields.insert("category".into(), json!(i)); + let first = effects_in_category(i).first().map(|e| e.id).unwrap_or(""); + set_selected(d, first); + set_stack_focus(d, false); + } + } + }); + ui.add_space(8.0); + ui.vertical(|ui| { + widgets::subheader(ui, tl!("Filters")); + ui.set_width(220.0); + egui::ScrollArea::vertical().max_height(220.0).show(ui, |ui| { + for e in &effects { + let picked = e.id == sel; + let name = tl!(e.label).trim_end_matches('…').to_string(); + let resp = ui.add(egui::Button::selectable(picked, name)); + if resp.clicked() { + set_selected(d, e.id); + set_stack_focus(d, false); + } + if resp.double_clicked() { + set_selected(d, e.id); + set_stack_focus(d, false); + add_selected(d, e.id); + stack = stack_of(d); + } + } + }); + if widgets::flat_button(ui, tl!("Add to Stack"), 120.0).clicked() && !sel.is_empty() { + add_selected(d, &sel); + stack = stack_of(d); + } + }); + }); + + ui.add_space(10.0); + widgets::subheader(ui, tl!("Applied Filters")); + let mut stack_sel = d.fields.get("stackSelected").and_then(Value::as_u64).unwrap_or(0) as usize; + if stack_sel >= stack.len() && !stack.is_empty() { + stack_sel = stack.len() - 1; + } + enum StackOp { + Toggle(usize), + Up(usize), + Down(usize), + Remove(usize), + Select(usize), + } + let mut op = None; + let stack_len = stack.len(); + egui::ScrollArea::vertical().max_height(120.0).show(ui, |ui| { + if stack.is_empty() { + ui.label(egui::RichText::new(tl!("Add filters from the list above")).color(t.text_dim)); + return; + } + for (i, entry) in stack.iter().enumerate().take(stack_len) { + ui.horizontal(|ui| { + let eye = if entry.visible { "eye" } else { "eye-off" }; + if widgets::icon_button(ui, eye, tl!("Click to toggle visibility"), false, 22.0).clicked() { + op = Some(StackOp::Toggle(i)); + } + let name = effect_label(&entry.effect); + if ui.add(egui::Button::selectable(stack_sel == i, name)).clicked() { + op = Some(StackOp::Select(i)); + } + if widgets::icon_button_enabled(ui, "dc-arrow-up", tl!("Move Up"), false, i > 0, 22.0).clicked() { + op = Some(StackOp::Up(i)); + } + if widgets::icon_button_enabled(ui, "dc-arrow-down", tl!("Move Down"), false, i + 1 < stack_len, 22.0).clicked() { + op = Some(StackOp::Down(i)); + } + if widgets::flat_button(ui, tl!("Remove"), 64.0).clicked() { + op = Some(StackOp::Remove(i)); + } + }); + } + }); + match op { + Some(StackOp::Toggle(i)) if stack.get(i).is_some() => stack[i].visible = !stack[i].visible, + Some(StackOp::Select(i)) => { + stack_sel = i; + set_stack_focus(d, true); + } + Some(StackOp::Up(i)) if i > 0 => { + stack.swap(i, i - 1); + stack_sel = i - 1; + } + Some(StackOp::Down(i)) if i + 1 < stack.len() => { + stack.swap(i, i + 1); + stack_sel = i + 1; + } + Some(StackOp::Remove(i)) if i < stack.len() => { + stack.remove(i); + if stack_sel >= stack.len() && !stack.is_empty() { + stack_sel = stack.len() - 1; + } + } + _ => {} + } + write_stack(d, &stack); + d.fields.insert("stackSelected".into(), json!(stack_sel)); + stack_preview_row(app, ui, d, &stack); + false +} + +fn add_selected(d: &mut Dialog, id: &str) { + let params = + if !stack_focus(d) && d.fields.get("browseEffect").and_then(Value::as_str) == Some(id) { browse_params(d) } else { default_params(id) }; + let mut stack = stack_of(d); + stack.push(StackEntry { effect: id.to_string(), visible: true, params }); + write_stack(d, &stack); + d.fields.insert("stackSelected".into(), json!(stack.len().saturating_sub(1))); + set_stack_focus(d, true); +} + +fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { + let stack = stack_of(d); + if stack.is_empty() { + if app.session.in_interaction() { + let _ = app.session.cancel_interaction(); + } + app.ui.dialog = None; + return Ok(Value::Null); + } + let batch = stack_batch_params(&stack); + if d.bool("preview") && app.session.in_interaction() { + return super::form::commit_preview(app, "command.batch", batch); + } + let r = app.run("command.batch", batch); + if r.is_ok() { + app.ui.dialog = None; + } + r +} + +#[cfg(test)] +mod tests { + use serde_json::json; + use vectorcraft_engine::Session; + + use crate::VectorcraftApp; + + fn frame(app: &mut VectorcraftApp) { + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + crate::theme::apply(&ctx, Default::default()); + let raw = egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1200.0, 900.0))), ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| crate::dialogs::show(app, ui.ctx())); + out.textures_delta.clear(); + } + + #[test] + fn stack_preview_is_one_interaction() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 200})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 40, "height": 40})).unwrap()["id"].as_u64().unwrap(); + app.run("ui.effectGalleryDialog", json!({})).unwrap(); + let d = app.ui.dialog.as_mut().unwrap(); + d.fields.insert("stack".into(), json!([{ "effect": "artistic.cutout", "visible": true, "params": { "levels": 3 } }])); + frame(&mut app); + assert!(app.session.in_interaction(), "the stack previews on the canvas"); + crate::dialogs::cancel(&mut app); + assert!(!app.session.in_interaction()); + assert!(app.session.active().unwrap().doc.node(vectorcraft_doc::NodeId(id)).unwrap().appearance.effects.is_empty()); + } + + #[test] + fn stack_ok_is_one_undo_step() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 200})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 40, "height": 40})).unwrap()["id"].as_u64().unwrap(); + app.run("ui.effectGalleryDialog", json!({})).unwrap(); + assert_eq!(app.ui.dialog.as_ref().map(|d| d.kind.as_str()), Some(super::KIND)); + let d = app.ui.dialog.as_mut().unwrap(); + d.fields.insert( + "stack".into(), + json!([ + { "effect": "artistic.cutout", "visible": true, "params": { "levels": 3 } }, + { "effect": "stylize.glowingEdges", "visible": false, "params": { "edgeWidth": 2 } } + ]), + ); + frame(&mut app); + let steps = app.session.active().unwrap().history.undo.len(); + crate::dialogs::confirm(&mut app).unwrap(); + let st = app.session.active().unwrap(); + let n = st.doc.node(vectorcraft_doc::NodeId(id)).unwrap(); + assert_eq!(n.appearance.effects.len(), 2); + assert_eq!(n.appearance.effects[0].id, "artistic.cutout"); + assert_eq!(n.appearance.effects[0].params.get("levels").and_then(|v| v.as_i64()), Some(3)); + assert_eq!(n.appearance.effects[1].id, "stylize.glowingEdges"); + assert!(!n.appearance.effects[1].visible); + assert_eq!(st.history.undo.len(), steps + 1); + } +} diff --git a/crates/ui-egui/src/dialogs/mod.rs b/crates/ui-egui/src/dialogs/mod.rs index f02f75f48..ac2c422fb 100644 --- a/crates/ui-egui/src/dialogs/mod.rs +++ b/crates/ui-egui/src/dialogs/mod.rs @@ -24,6 +24,7 @@ pub mod dxf_import; pub mod dxf_options; pub mod edit_selection; mod effect; +pub mod effect_gallery; pub mod envelope; pub mod eps_options; pub mod expand; @@ -301,6 +302,7 @@ registry! { SwatchConflict: [swatch_conflict::KIND] => swatch_conflict::SPEC, FileInfo: [file_info::KIND] => file_info::SPEC, RasterEffectsSettings: [raster_effects::KIND] => raster_effects::SPEC, + EffectGallery: [effect_gallery::KIND] => effect_gallery::SPEC, MissingLinks: [missing_links::KIND] => missing_links::SPEC, MissingFonts: [missing_fonts::KIND] => missing_fonts::SPEC, FindLinks: [find_links::KIND] => find_links::SPEC, diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index a44438be7..0ab57b7a3 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5310,6 +5310,14 @@ theme Appearance Външен вид Full Justify Пълно двустранно Minimum must not exceed Desired, nor Desired Maximum. Минималната стойност не може да надвишава желаната, нито желаната – максималната. Justification Подравняване + Add filters from the list above Добавете филтри от списъка по-горе + Add to Stack Добавяне към стека + Applied Filters Приложени филтри + Categories Категории + Choose a filter Изберете филтър + Effect Gallery Галерия с ефекти + Filters Филтри + Select artwork to apply effects Изберете графика, за да приложите ефекти @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them променливите и наборите от данни на файла не бяха прочетени (променливи: {variables}, набори от данни: {data_sets}): панелът „Променливи“ не ги показва # Select by Color (select.color) Select by Color Избиране по цвят @@ -5343,3 +5351,5 @@ theme Appearance Външен вид Shift: Изместване: Fresco… Фреска… Fresco Фреска + Effect Gallery… Галерия с ефекти… + Effect Gallery Dialog Диалог „Галерия с ефекти“ diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 0bed38936..54f38c94b 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5353,6 +5353,14 @@ mark-style Japanese Japonské Full Justify Zarovnat do bloku Minimum must not exceed Desired, nor Desired Maximum. Minimum nesmí překročit požadovanou hodnotu ani požadovaná hodnota maximum. Justification Zarovnání do bloku + Add filters from the list above Přidejte filtry ze seznamu výše + Add to Stack Přidat na zásobník + Applied Filters Použité filtry + Categories Kategorie + Choose a filter Vyberte filtr + Effect Gallery Galerie efektů + Filters Filtry + Select artwork to apply effects Vyberte grafiku, na kterou chcete použít efekty @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them proměnné a sady dat souboru nebyly načteny (proměnné: {variables}, sady dat: {data_sets}): panel Proměnné je nezobrazuje # Select by Color (select.color) Select by Color Vybrat podle barvy @@ -5386,3 +5394,5 @@ mark-style Japanese Japonské Shift: Posun: Fresco… Freska… Fresco Freska + Effect Gallery… Galerie efektů… + Effect Gallery Dialog Dialog Galerie efektů diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index e5c9c6a74..14bdad93a 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5306,6 +5306,14 @@ swatch Bright Violet Kraftig violet Full Justify Lige margener Minimum must not exceed Desired, nor Desired Maximum. Minimum må ikke overstige Ønsket, og Ønsket ikke Maksimum. Justification Justering + Add filters from the list above Tilføj filtre fra listen ovenfor + Add to Stack Tilføj til stak + Applied Filters Anvendte filtre + Categories Kategorier + Choose a filter Vælg et filter + Effect Gallery Effektgalleri + Filters Filtre + Select artwork to apply effects Vælg grafik for at anvende effekter # Select by Color (select.color) Select by Color Markér efter farve Smudge Stick… Udtværingsstift… @@ -5359,3 +5367,5 @@ swatch Bright Violet Kraftig violet Shift: Forskydning: Fresco… Fresko… Fresco Fresko + Effect Gallery… Effektgalleri… + Effect Gallery Dialog Dialogboksen Effektgalleri diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index a356bacb5..6aacd6e3c 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4934,6 +4934,14 @@ effect Soft Weich Minimum must not exceed Desired, nor Desired Maximum. Das Minimum darf den optimalen Wert nicht übersteigen, der optimale Wert nicht das Maximum. Minimum Minimum Justification Abstände + Add filters from the list above Filter aus der Liste oben hinzufügen + Add to Stack Zum Stapel hinzufügen + Applied Filters Angewendete Filter + Categories Kategorien + Choose a filter Filter wählen + Effect Gallery Effekt-Galerie + Filters Filter + Select artwork to apply effects Wählen Sie Grafik aus, um Effekte anzuwenden @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them die Variablen und Datensätze der Datei wurden nicht gelesen (Variablen: {variables}, Datensätze: {data_sets}): Das Bedienfeld „Variablen“ listet sie nicht auf # Select by Color (select.color) Select by Color Nach Farbe auswählen @@ -4967,3 +4975,5 @@ effect Soft Weich Shift: Verschiebung: Fresco… Fresko… Fresco Fresko + Effect Gallery… Effekt-Galerie… + Effect Gallery Dialog Dialogfeld „Effekt-Galerie“ diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 47459f07c..2ea3887d3 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5305,6 +5305,14 @@ effect Points Σημεία Full Justify Στοίχιση και στα δύο άκρα Minimum must not exceed Desired, nor Desired Maximum. Το ελάχιστο δεν πρέπει να υπερβαίνει το επιθυμητό, ούτε το επιθυμητό το μέγιστο. Justification Πλήρης στοίχιση + Add filters from the list above Προσθέστε φίλτρα από τη λίστα παραπάνω + Add to Stack Προσθήκη στη στοίβα + Applied Filters Εφαρμοσμένα φίλτρα + Categories Κατηγορίες + Choose a filter Επιλέξτε φίλτρο + Effect Gallery Συλλογή εφέ + Filters Φίλτρα + Select artwork to apply effects Επιλέξτε γραφικά για να εφαρμόσετε εφέ @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them οι μεταβλητές και τα σύνολα δεδομένων του αρχείου δεν διαβάστηκαν (μεταβλητές: {variables}, σύνολα δεδομένων: {data_sets}): ο πίνακας Μεταβλητές δεν τα εμφανίζει # Select by Color (select.color) Select by Color Επιλογή βάσει χρώματος @@ -5338,3 +5346,5 @@ effect Points Σημεία Shift: Μετατόπιση: Fresco… Νωπογραφία… Fresco Νωπογραφία + Effect Gallery… Συλλογή εφέ… + Effect Gallery Dialog Παράθυρο διαλόγου Συλλογή εφέ diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index db7a047b7..00a483a19 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5295,6 +5295,14 @@ mark-style Japanese Japonesas Full Justify Justificar completamente Minimum must not exceed Desired, nor Desired Maximum. El mínimo no puede superar el deseado, ni el deseado el máximo. Justification Justificación + Add filters from the list above Añada filtros de la lista de arriba + Add to Stack Añadir a la pila + Applied Filters Filtros aplicados + Categories Categorías + Choose a filter Elija un filtro + Effect Gallery Galería de efectos + Filters Filtros + Select artwork to apply effects Seleccione gráficos para aplicar efectos @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them no se leyeron las variables ni los conjuntos de datos del archivo (variables: {variables}, conjuntos de datos: {data_sets}): el panel Variables no los muestra # Select by Color (select.color) Select by Color Seleccionar por color @@ -5328,3 +5336,5 @@ mark-style Japanese Japonesas Shift: Desplazamiento: Fresco… Fresco… Fresco Fresco + Effect Gallery… Galería de efectos… + Effect Gallery Dialog Cuadro de diálogo Galería de efectos diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index d04facf00..19db20bff 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5317,6 +5317,14 @@ mark-style Japanese Japanilainen Full Justify Täysi tasaus Minimum must not exceed Desired, nor Desired Maximum. Minimi ei saa ylittää haluttua arvoa eikä haluttu arvo maksimia. Justification Tasaus + Add filters from the list above Lisää suodattimia yllä olevasta luettelosta + Add to Stack Lisää pinoon + Applied Filters Käytetyt suodattimet + Categories Luokat + Choose a filter Valitse suodatin + Effect Gallery Tehostegalleria + Filters Suodattimet + Select artwork to apply effects Valitse grafiikka, johon tehosteet otetaan @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them tiedoston muuttujia ja tietojoukkoja ei luettu (muuttujia: {variables}, tietojoukkoja: {data_sets}): Muuttujat-paneeli ei näytä niitä # Select by Color (select.color) Select by Color Valitse värin mukaan @@ -5417,3 +5425,5 @@ swatch Bright Violet Kirkas violetti Shift: Siirtymä: Fresco… Fresko… Fresco Fresko + Effect Gallery… Tehostegalleria… + Effect Gallery Dialog Tehostegalleria-valintaikkuna diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 9993f344e..a1abcc6ba 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5352,6 +5352,14 @@ theme Appearance Apparence Full Justify Justification complète Minimum must not exceed Desired, nor Desired Maximum. Le minimum ne doit pas dépasser la valeur optimale, ni la valeur optimale le maximum. Justification Justification + Add filters from the list above Ajoutez des filtres à partir de la liste ci-dessus + Add to Stack Ajouter à la pile + Applied Filters Filtres appliqués + Categories Catégories + Choose a filter Choisir un filtre + Effect Gallery Galerie d'effets + Filters Filtres + Select artwork to apply effects Sélectionnez des éléments graphiques pour appliquer des effets @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them les variables et jeux de données du fichier n’ont pas été lus (variables : {variables}, jeux de données : {data_sets}) : le panneau Variables ne les affiche pas # Select by Color (select.color) Select by Color Sélectionner par couleur @@ -5385,3 +5393,5 @@ theme Appearance Apparence Shift: Décalage : Fresco… Fresque… Fresco Fresque + Effect Gallery… Galerie d'effets… + Effect Gallery Dialog Boîte de dialogue Galerie d'effets diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index f9f65f9fe..448ee605f 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5339,6 +5339,14 @@ imageTrace Outline Körvonal Full Justify Teljes sorkizárás Minimum must not exceed Desired, nor Desired Maximum. A minimum nem haladhatja meg a kívánt értéket, sem a kívánt érték a maximumot. Justification Sorkizárás + Add filters from the list above Adjon hozzá szűrőket a fenti listából + Add to Stack Hozzáadás a veremhez + Applied Filters Alkalmazott szűrők + Categories Kategóriák + Choose a filter Válasszon szűrőt + Effect Gallery Effektusgaléria + Filters Szűrők + Select artwork to apply effects Válasszon ki grafikát az effektusok alkalmazásához @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them a fájl változói és adatkészletei nem lettek beolvasva (változók: {variables}, adatkészletek: {data_sets}): a Változók panel nem listázza őket # Select by Color (select.color) Select by Color Kijelölés szín szerint @@ -5372,3 +5380,5 @@ imageTrace Outline Körvonal Shift: Eltolás: Fresco… Freskó… Fresco Freskó + Effect Gallery… Effektusgaléria… + Effect Gallery Dialog Effektusgaléria párbeszédpanel diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 17c6c7ceb..917c48816 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5306,6 +5306,14 @@ swatch Bright Violet Violet cerah Full Justify Rata Penuh Minimum must not exceed Desired, nor Desired Maximum. Minimum tidak boleh melebihi Diinginkan, dan Diinginkan tidak boleh melebihi Maksimum. Justification Justifikasi + Add filters from the list above Tambahkan filter dari daftar di atas + Add to Stack Tambahkan ke Tumpukan + Applied Filters Filter yang Diterapkan + Categories Kategori + Choose a filter Pilih filter + Effect Gallery Galeri Efek + Filters Filter + Select artwork to apply effects Pilih grafis untuk menerapkan efek # Select by Color (select.color) Select by Color Pilih berdasarkan warna Smudge Stick… Tongkat olesan… @@ -5359,3 +5367,5 @@ swatch Bright Violet Violet cerah Shift: Pergeseran: Fresco… Fresko… Fresco Fresko + Effect Gallery… Galeri Efek… + Effect Gallery Dialog Dialog Galeri Efek diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 7333cb719..ac59807ef 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4968,6 +4968,14 @@ lighting Light Luce Minimum must not exceed Desired, nor Desired Maximum. Il minimo non può superare il valore desiderato, né il desiderato il massimo. Minimum Minimo Justification Giustificazione + Add filters from the list above Aggiungi filtri dall'elenco sopra + Add to Stack Aggiungi alla pila + Applied Filters Filtri applicati + Categories Categorie + Choose a filter Scegli un filtro + Effect Gallery Galleria effetti + Filters Filtri + Select artwork to apply effects Seleziona la grafica a cui applicare gli effetti @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them le variabili e i set di dati del file non sono stati letti (variabili: {variables}, set di dati: {data_sets}): il pannello Variabili non li elenca # Select by Color (select.color) Select by Color Seleziona per colore @@ -5001,3 +5009,5 @@ lighting Light Luce Shift: Spostamento: Fresco… Affresco… Fresco Affresco + Effect Gallery… Galleria effetti… + Effect Gallery Dialog Finestra di dialogo Galleria effetti diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index e58f4e0d7..fd68fd1f9 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5008,6 +5008,14 @@ lighting Light ライト Minimum must not exceed Desired, nor Desired Maximum. 最小値は最適値を、最適値は最大値を超えることはできません。 Minimum 最小 Justification 文字組み + Add filters from the list above 上のリストからフィルターを追加 + Add to Stack スタックに追加 + Applied Filters 適用したフィルター + Categories カテゴリ + Choose a filter フィルターを選択 + Effect Gallery 効果ギャラリー + Filters フィルター + Select artwork to apply effects 効果を適用するグラフィックを選択してください @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them ファイルの変数とデータセットは読み込まれませんでした (変数: {variables}、データセット: {data_sets})。変数パネルには表示されません # Select by Color (select.color) Select by Color カラーで選択 @@ -5048,3 +5056,5 @@ lighting Light ライト Shift: シフト: Fresco… フレスコ… Fresco フレスコ + Effect Gallery… 効果ギャラリー… + Effect Gallery Dialog 効果ギャラリーダイアログ diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index abebbb73d..db1915b3b 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5296,3 +5296,12 @@ imageTrace Outline კონტური Shift: წანაცვლება: Fresco… ფრესკა… Fresco ფრესკა + Effect Gallery ეფექტების გალერეა + Add filters from the list above დაამატეთ ფილტრები ზემოთ მოცემული სიიდან + Add to Stack დასტაში დამატება + Applied Filters გამოყენებული ფილტრები + Categories კატეგორიები + Filters ფილტრები + Select artwork to apply effects აირჩიეთ ნამუშევარი ეფექტების გამოსაყენებლად + Effect Gallery… ეფექტების გალერეა… + Effect Gallery Dialog ეფექტების გალერეა დიალოგი diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index eb4baa015..2337fad72 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4931,6 +4931,14 @@ lighting Light 광원 Minimum must not exceed Desired, nor Desired Maximum. 최소값은 희망값을, 희망값은 최대값을 넘을 수 없습니다. Minimum 최소 Justification 양끝 맞춤 + Add filters from the list above 위 목록에서 필터 추가 + Add to Stack 스택에 추가 + Applied Filters 적용된 필터 + Categories 범주 + Choose a filter 필터 선택 + Effect Gallery 효과 갤러리 + Filters 필터 + Select artwork to apply effects 효과를 적용할 그래픽을 선택하세요 @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them 파일의 변수와 데이터 세트를 읽지 않았습니다(변수: {variables}, 데이터 세트: {data_sets}). 변수 패널에 표시되지 않습니다 # Select by Color (select.color) Select by Color 색상별 선택 @@ -4964,3 +4972,5 @@ lighting Light 광원 Shift: 이동: Fresco… 프레스코… Fresco 프레스코 + Effect Gallery… 효과 갤러리… + Effect Gallery Dialog 효과 갤러리 대화 상자 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 70ffeafc6..a0040b531 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5348,6 +5348,14 @@ swatch Bright Violet Felviolet Full Justify Volledig uitvullen Minimum must not exceed Desired, nor Desired Maximum. Minimum mag niet groter zijn dan Gewenst, en Gewenst niet groter dan Maximum. Justification Uitvullen + Add filters from the list above Voeg filters toe uit de lijst hierboven + Add to Stack Toevoegen aan stapel + Applied Filters Toegepaste filters + Categories Categorieën + Choose a filter Kies een filter + Effect Gallery Effectgalerij + Filters Filters + Select artwork to apply effects Selecteer grafiek om effecten toe te passen # Select by Color (select.color) Select by Color Selecteren op kleur Smudge Stick… Uitsmeerstift… @@ -5401,3 +5409,5 @@ swatch Bright Violet Felviolet Shift: Verschuiving: Fresco… Fresco… Fresco Fresco + Effect Gallery… Effectgalerij… + Effect Gallery Dialog Dialoogvenster Effectgalerij diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 1192bb98f..761bb0dae 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5205,6 +5205,14 @@ mark-style Japanese Japońskie Full Justify Pełne justowanie Minimum must not exceed Desired, nor Desired Maximum. Minimum nie może przekraczać wartości optymalnej, a wartość optymalna maksimum. Justification Justowanie + Add filters from the list above Dodaj filtry z listy powyżej + Add to Stack Dodaj do stosu + Applied Filters Zastosowane filtry + Categories Kategorie + Choose a filter Wybierz filtr + Effect Gallery Galeria efektów + Filters Filtry + Select artwork to apply effects Wybierz grafikę, aby zastosować efekty @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them zmienne i zestawy danych pliku nie zostały wczytane (zmienne: {variables}, zestawy danych: {data_sets}): panel Zmienne ich nie wyświetla # Select by Color (select.color) Select by Color Zaznacz według koloru @@ -5238,3 +5246,5 @@ mark-style Japanese Japońskie Shift: Przesunięcie: Fresco… Fresk… Fresco Fresk + Effect Gallery… Galeria efektów… + Effect Gallery Dialog Okno dialogowe Galeria efektów diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 0af3fe6c2..e40d6801b 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5270,6 +5270,14 @@ mark-style Japanese Japonesas Full Justify Justificação Completa Minimum must not exceed Desired, nor Desired Maximum. O mínimo não pode passar do desejado, nem o desejado do máximo. Justification Justificação + Add filters from the list above Adicione filtros da lista acima + Add to Stack Adicionar à Pilha + Applied Filters Filtros Aplicados + Categories Categorias + Choose a filter Escolha um filtro + Effect Gallery Galeria de Efeitos + Filters Filtros + Select artwork to apply effects Selecione a arte para aplicar efeitos @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them as variáveis e os conjuntos de dados do arquivo não foram lidos (variáveis: {variables}, conjuntos de dados: {data_sets}): o painel Variáveis não os lista # Select by Color (select.color) Select by Color Selecionar por Cor @@ -5303,3 +5311,5 @@ mark-style Japanese Japonesas Shift: Deslocamento: Fresco… Afresco… Fresco Afresco + Effect Gallery… Galeria de Efeitos… + Effect Gallery Dialog Caixa de diálogo Galeria de Efeitos diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index a1b7dd319..2925e2fd8 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4964,6 +4964,14 @@ lighting Light Свет Minimum must not exceed Desired, nor Desired Maximum. Минимум не может превышать желательное значение, а желательное — максимум. Minimum Минимум Justification Выключка + Add filters from the list above Добавьте фильтры из списка выше + Add to Stack Добавить в стек + Applied Filters Применённые фильтры + Categories Категории + Choose a filter Выберите фильтр + Effect Gallery Галерея эффектов + Filters Фильтры + Select artwork to apply effects Выберите графику, чтобы применить эффекты @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them переменные и наборы данных файла не прочитаны (переменных: {variables}, наборов данных: {data_sets}): панель «Переменные» их не показывает # Select by Color (select.color) Select by Color Выделить по цвету @@ -4997,3 +5005,5 @@ lighting Light Свет Shift: Сдвиг: Fresco… Фреска… Fresco Фреска + Effect Gallery… Галерея эффектов… + Effect Gallery Dialog Диалог «Галерея эффектов» diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 843b3cbfd..2ba5a4201 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5309,6 +5309,14 @@ theme Appearance Utseende Full Justify Fullständig marginaljustering Minimum must not exceed Desired, nor Desired Maximum. Minimum får inte överstiga Önskat, och Önskat inte Maximum. Justification Justering + Add filters from the list above Lägg till filter från listan ovan + Add to Stack Lägg till i stacken + Applied Filters Tillämpade filter + Categories Kategorier + Choose a filter Välj ett filter + Effect Gallery Effektgalleri + Filters Filter + Select artwork to apply effects Välj grafik för att tillämpa effekter @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them filens variabler och datauppsättningar lästes inte (variabler: {variables}, datauppsättningar: {data_sets}): panelen Variabler visar dem inte # Select by Color (select.color) Select by Color Markera efter färg @@ -5342,3 +5350,5 @@ theme Appearance Utseende Shift: Förskjutning: Fresco… Fresk… Fresco Fresk + Effect Gallery… Effektgalleri… + Effect Gallery Dialog Dialogrutan Effektgalleri diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index b422ebcad..e9fea9f52 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4948,6 +4948,14 @@ lighting Light Світло Minimum must not exceed Desired, nor Desired Maximum. Мінімум не може перевищувати бажане значення, а бажане — максимум. Minimum Мінімум Justification Вирівнювання за шириною + Add filters from the list above Додайте фільтри зі списку вище + Add to Stack Додати до стека + Applied Filters Застосовані фільтри + Categories Категорії + Choose a filter Виберіть фільтр + Effect Gallery Галерея ефектів + Filters Фільтри + Select artwork to apply effects Виберіть графіку, щоб застосувати ефекти @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them змінні та набори даних файлу не прочитано (змінних: {variables}, наборів даних: {data_sets}): панель «Змінні» їх не показує # Select by Color (select.color) Select by Color Виділити за кольором @@ -4981,3 +4989,5 @@ lighting Light Світло Shift: Зсув: Fresco… Фреска… Fresco Фреска + Effect Gallery… Галерея ефектів… + Effect Gallery Dialog Діалог «Галерея ефектів» diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 38ce6e879..1858de13b 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4948,6 +4948,14 @@ theme Appearance Giao diện Minimum must not exceed Desired, nor Desired Maximum. Giá trị tối thiểu không được vượt quá giá trị mong muốn, và giá trị mong muốn không được vượt quá tối đa. Minimum Tối thiểu Justification Căn đều + Add filters from the list above Thêm bộ lọc từ danh sách phía trên + Add to Stack Thêm vào ngăn xếp + Applied Filters Bộ lọc đã áp dụng + Categories Danh mục + Choose a filter Chọn bộ lọc + Effect Gallery Thư viện hiệu ứng + Filters Bộ lọc + Select artwork to apply effects Chọn đồ họa để áp dụng hiệu ứng @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them các biến và tập dữ liệu của tệp chưa được đọc (biến: {variables}, tập dữ liệu: {data_sets}): bảng Biến không liệt kê chúng # Select by Color (select.color) Select by Color Chọn theo màu @@ -4981,3 +4989,5 @@ theme Appearance Giao diện Shift: Dịch chuyển: Fresco… Bích họa… Fresco Bích họa + Effect Gallery… Thư viện hiệu ứng… + Effect Gallery Dialog Hộp thoại Thư viện hiệu ứng diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 4fb69a12d..849044e52 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5192,6 +5192,14 @@ lighting Light 光源 Minimum must not exceed Desired, nor Desired Maximum. 最小值不能超过所需值,所需值不能超过最大值。 Minimum 最小值 Justification 对齐设置 + Add filters from the list above 从上方列表添加滤镜 + Add to Stack 添加到堆栈 + Applied Filters 已应用的滤镜 + Categories 类别 + Choose a filter 选择滤镜 + Effect Gallery 效果库 + Filters 滤镜 + Select artwork to apply effects 选择要应用效果的图稿 @msg the file's variables and data sets weren't read (variables: {variables}, data sets: {data_sets}): the Variables panel doesn't list them 文件的变量和数据集未被读取(变量:{variables},数据集:{data_sets}):“变量”面板不会列出它们 # Select by Color (select.color) Select by Color 按颜色选择 @@ -5225,3 +5233,5 @@ lighting Light 光源 Shift: 偏移: Fresco… 壁画… Fresco 壁画 + Effect Gallery… 效果库… + Effect Gallery Dialog 效果库对话框 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 8a0043f58..05ac2082a 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -4982,6 +4982,14 @@ lighting Light 光源 Minimum must not exceed Desired, nor Desired Maximum. 最小值不可超過所需值,所需值不可超過最大值。 Minimum 最小值 Justification 對齊設定 + Add filters from the list above 從上方清單新增濾鏡 + Add to Stack 加入堆疊 + Applied Filters 已套用的濾鏡 + Categories 類別 + Choose a filter 選擇濾鏡 + Effect Gallery 效果庫 + Filters 濾鏡 + Select artwork to apply effects 選取要套用效果的圖稿 # Select by Color (select.color) Select by Color 依顏色選取 @msg missing integer `{key}` 未提供整數 `{key}` @@ -5020,3 +5028,5 @@ lighting Light 光源 Shift: 偏移: Fresco… 壁畫… Fresco 壁畫 + Effect Gallery… 效果庫… + Effect Gallery Dialog 效果庫對話框 diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 76f0b3176..16d1fd808 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -857,6 +857,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{} open Document Raster Effects Settings (dialog `rasterEffectsSettings`: the document.rasterEffectsSettings fields); OK runs it. The menu item opens it too", ), + ( + "ui.effectGalleryDialog", + "Effect Gallery Dialog", + "", + "{} open Effect Gallery (dialog `effectGallery`: browse raster filters, build a stack, OK applies it as separate effects in one undo step). Effect → Effect Gallery… opens it too", + ), ( "ui.pdfPresetsDialog", "PDF Presets…", @@ -1770,6 +1776,7 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option io::export(app, s("format").as_deref(), s("path"), p), "ui.fileInfoDialog" => crate::dialogs::file_info::open(app), "ui.rasterEffectsSettingsDialog" => crate::dialogs::raster_effects::open(app), + "ui.effectGalleryDialog" => crate::dialogs::effect_gallery::open(app, p), "ui.pdfPresetsDialog" => { crate::dialogs::pdf_presets::open(app, s("selected").as_deref()); Ok(Value::Null) @@ -2436,7 +2443,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool { id if id.starts_with("view.goto") => { id["view.goto".len()..].parse::().is_ok_and(|n| n >= 1 && app.session.active().is_some_and(|d| n <= d.doc.views.len())) } - "effect.dialog" | "ui.recolorDialog" => app.session.active().is_some_and(|d| !d.selection.is_empty()), + "effect.dialog" | "ui.recolorDialog" | "ui.effectGalleryDialog" => app.session.active().is_some_and(|d| !d.selection.is_empty()), "ui.cropImage" => selected_image(app, |_| true), "effect.applyLast" | "effect.last" => app.last_effect.is_some() && app.session.active().is_some_and(|d| !d.selection.is_empty()), "file.export.pdf" | "ui.savePdfDialog" | "ui.fileInfoDialog" | "ui.rasterEffectsSettingsDialog" => app.session.active().is_some(), @@ -4055,6 +4062,7 @@ fn effect_menu() -> Vec { if !raster.is_empty() { out.push(Sep); out.push(Item::Header("Raster Effects")); + out.push(c("Effect Gallery…", "ui.effectGalleryDialog")); out.extend(raster); } // Anything not placed above (future effects) still shows up.