mirror of
https://github.com/storytold/filmcraft.git
synced 2026-11-02 18:09:21 +00:00
refactor(ui): use craft-ui's ComboBox for every dropdown (#3445)
* ui-egui: use craft_ui::ComboBox for dropdowns; repin craft-libs to 62bb0e1 * chore(deps): craft-libs 60d60d48 (dropdown fixes) * chore(deps): craft-libs 6af63481 (dropdown fixes) * chore(deps): craft-libs 4d7b82f7 (dropdown fixes) * chore(deps): craft-libs 92d392cf (#387 merged)
This commit is contained in:
Generated
+6
-6
@@ -1061,17 +1061,17 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-docpath"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
|
||||
[[package]]
|
||||
name = "craft-fonts-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
|
||||
[[package]]
|
||||
name = "craft-i18n"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"fixed_decimal",
|
||||
"icu_decimal",
|
||||
@@ -1094,7 +1094,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
@@ -1102,7 +1102,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"craft-fonts-build",
|
||||
"craft-i18n",
|
||||
@@ -1123,7 +1123,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-update"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dunce",
|
||||
|
||||
+4
-4
@@ -72,19 +72,19 @@ chrono = { version = "0.4.45", default-features = false, features = ["std"] }
|
||||
rayon = "1"
|
||||
proptest = "1"
|
||||
roxmltree = "0.21"
|
||||
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7" }
|
||||
craft-media = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7", default-features = false }
|
||||
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
craft-media = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", default-features = false }
|
||||
|
||||
# ui
|
||||
# Shared frontend components; keep this dependency above the engine boundary.
|
||||
craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7", features = ["serde", "native-windows"] }
|
||||
craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["serde", "native-windows"] }
|
||||
# egui 0.37: bundled fonts are opt-in, and the monochrome symbol/emoji faces moved to their own
|
||||
# feature. FilmCraft paints ✔ ✓ ✖ ⚡ ⚠ ⚙ ♪ ☑ in panel chrome, so both features are part of the
|
||||
# production font policy (system_fonts stays off).
|
||||
egui = { version = "0.37", features = ["default_fonts", "monochrome_emoji_fonts"] }
|
||||
eframe = { version = "0.37", default-features = false }
|
||||
egui_kittest = "0.37"
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7", features = ["egui"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["egui"] }
|
||||
rfd = "0.17"
|
||||
|
||||
[workspace.lints.rust]
|
||||
|
||||
@@ -2,3 +2,7 @@ allow-unwrap-in-tests = true
|
||||
allow-expect-in-tests = true
|
||||
allow-panic-in-tests = true
|
||||
allow-indexing-slicing-in-tests = true
|
||||
|
||||
disallowed-types = [
|
||||
{ path = "egui::ComboBox", reason = "use craft_ui::ComboBox: egui's combo box menu can't grow back while open (egui#5225)" },
|
||||
]
|
||||
|
||||
@@ -244,7 +244,7 @@ pub fn contributors_ui(ui: &mut egui::Ui, t: &Tokens, auto: &mut Registry) {
|
||||
}
|
||||
ui.separator();
|
||||
ui.label(tl!("Sort"));
|
||||
let combo = egui::ComboBox::from_id_salt("credits_sort").selected_text(crate::i18n::t(v.key.label().0)).show_ui(ui, |ui| {
|
||||
let combo = craft_ui::ComboBox::from_id_salt("credits_sort").selected_text(crate::i18n::t(v.key.label().0)).show_ui(ui, |ui| {
|
||||
for k in SortKey::ALL {
|
||||
if ui.selectable_label(v.key == k, crate::i18n::t(k.label().0)).clicked() {
|
||||
v.key = k;
|
||||
|
||||
@@ -246,7 +246,7 @@ fn add_tracks(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
let list_w = ui.available_width();
|
||||
// nowhere to choose from while the sequence has no track of the kind
|
||||
ui.add_enabled_ui(!tracks.is_empty(), |ui| {
|
||||
let r = egui::ComboBox::from_id_salt(("add-tracks-placement", kind)).selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("add-tracks-placement", kind)).selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
for n in 0..=tracks.len() {
|
||||
let label = place(n);
|
||||
let o = ui.selectable_value(after, n, &label);
|
||||
@@ -261,7 +261,7 @@ fn add_tracks(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
label(ui, tl!("Track type"));
|
||||
let shown = types.iter().find(|(id, _)| *id == chosen.as_str()).map_or(types[0].1, |(_, label)| label).to_string();
|
||||
let list_w = ui.available_width();
|
||||
let r = egui::ComboBox::from_id_salt(("add-tracks-type", kind)).selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("add-tracks-type", kind)).selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
for (id, label) in types {
|
||||
let o = ui.selectable_value(chosen, id.to_string(), *label);
|
||||
elems.push((format!("addTracks.{kind}.type.option.{id}"), o.rect, label.to_string()));
|
||||
@@ -355,7 +355,7 @@ fn delete_tracks(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
ui.add_space(22.0);
|
||||
let shown = if target == "empty" { tl!("All Empty Tracks").to_string() } else { target.clone() };
|
||||
ui.add_enabled_ui(*on, |ui| {
|
||||
let r = egui::ComboBox::from_id_salt(("delete-tracks", kind)).selected_text(&shown).width(170.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("delete-tracks", kind)).selected_text(&shown).width(170.0).show_ui(ui, |ui| {
|
||||
let e = ui.selectable_value(target, "empty".to_string(), tl!("All Empty Tracks"));
|
||||
elems.push((format!("deleteTracks.{kind}.option.empty"), e.rect, "All Empty Tracks".into()));
|
||||
for n in list {
|
||||
|
||||
@@ -651,7 +651,7 @@ pub(crate) fn param_row(
|
||||
fit_to_row(&mut vui);
|
||||
// (narrower when the effect list is narrow, so it stays left of the divider)
|
||||
let w = vui.available_width().min(130.0);
|
||||
let combo = egui::ComboBox::from_id_salt(id).selected_text(opts.get(sel).map_or("", |o| crate::i18n::t(o))).width(w).show_ui(&mut vui, |ui| {
|
||||
let combo = craft_ui::ComboBox::from_id_salt(id).selected_text(opts.get(sel).map_or("", |o| crate::i18n::t(o))).width(w).show_ui(&mut vui, |ui| {
|
||||
for (i, o) in opts.iter().enumerate() {
|
||||
if ui.selectable_value(&mut sel, i, crate::i18n::t(o)).changed() {
|
||||
set = Some(json!(i));
|
||||
|
||||
@@ -214,7 +214,7 @@ fn typed_body(ui: &mut egui::Ui, cx: &mut Ctx, st: &EssentialSound, items: &[Tra
|
||||
let mut draft: Option<String> = ui.data(|d| d.get_temp(draft_id));
|
||||
ui.horizontal(|ui| {
|
||||
let w = ui.available_width() - 56.0;
|
||||
let cb = egui::ComboBox::from_id_salt("es-preset").selected_text(¤t).width(w).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("es-preset").selected_text(¤t).width(w).show_ui(ui, |ui| {
|
||||
for p in &mine {
|
||||
if ui.selectable_label(p.name == st.preset, &p.name).clicked() {
|
||||
picked = Some(p.name.clone());
|
||||
@@ -563,13 +563,14 @@ fn slider_track(ui: &mut egui::Ui, cx: &mut Ctx, track: Rect, key: &str, v: f64,
|
||||
fn combo(ui: &mut egui::Ui, cx: &mut Ctx, key: &str, names: &[&str], current: &str, active: bool) {
|
||||
let mut picked = None;
|
||||
ui.add_enabled_ui(active, |ui| {
|
||||
let cb = egui::ComboBox::from_id_salt(("es-combo", key)).selected_text(crate::i18n::t(current)).width(ui.available_width() - 4.0).show_ui(ui, |ui| {
|
||||
for n in names {
|
||||
if ui.selectable_label(*n == current, crate::i18n::t(n)).clicked() {
|
||||
picked = Some(n.to_string());
|
||||
let cb =
|
||||
craft_ui::ComboBox::from_id_salt(("es-combo", key)).selected_text(crate::i18n::t(current)).width(ui.available_width() - 4.0).show_ui(ui, |ui| {
|
||||
for n in names {
|
||||
if ui.selectable_label(*n == current, crate::i18n::t(n)).clicked() {
|
||||
picked = Some(n.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
cx.auto.add(&format!("essentialSound.{key}"), cb.response.rect, key);
|
||||
});
|
||||
if let Some(n) = picked {
|
||||
|
||||
@@ -67,7 +67,7 @@ pub fn events(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
Level::Warning => tl!("Warnings and Errors"),
|
||||
Level::Error => tl!("Errors Only"),
|
||||
};
|
||||
let r = egui::ComboBox::from_id_salt("events-filter").selected_text(label(min)).width(150.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("events-filter").selected_text(label(min)).width(150.0).show_ui(ui, |ui| {
|
||||
for l in [Level::Info, Level::Warning, Level::Error] {
|
||||
let r = ui.selectable_label(min == l, label(l));
|
||||
elems.push((format!("events.filter.{}", l.label().to_ascii_lowercase()), r.rect, label(l).into()));
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(super) fn combo(ui: &mut egui::Ui, reg: &mut Reg, id: &str, selected: &str,
|
||||
/// [`combo`] whose options carry why they are greyed out (label, `Some(reason)` when disabled).
|
||||
fn combo_why(ui: &mut egui::Ui, reg: &mut Reg, id: &str, selected: &str, options: &[(String, Option<&str>)], width: f32) -> Option<usize> {
|
||||
let mut chosen = None;
|
||||
let r = egui::ComboBox::from_id_salt(id).selected_text(crate::i18n::t(selected)).width(width).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(id).selected_text(crate::i18n::t(selected)).width(width).show_ui(ui, |ui| {
|
||||
for (i, (label, why)) in options.iter().enumerate() {
|
||||
let enabled = why.is_none();
|
||||
let resp = ui.add_enabled(enabled, egui::Button::selectable(label == selected, crate::i18n::t(label)));
|
||||
@@ -247,8 +247,9 @@ pub(super) fn settings_column(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: R
|
||||
});
|
||||
// Preset: favourites, then every preset by category, then More presets…
|
||||
row(ui, &t, tl!("Preset"), |ui| {
|
||||
let r =
|
||||
egui::ComboBox::from_id_salt("export.preset").selected_text(crate::i18n::t(&ex.preset)).width(ui.available_width() - 4.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("export.preset").selected_text(crate::i18n::t(&ex.preset)).width(ui.available_width() - 4.0).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
if !favs.is_empty() {
|
||||
ui.label(egui::RichText::new(tl!("Favorites")).color(t.text_dim).size(11.0));
|
||||
for f in &favs {
|
||||
@@ -272,7 +273,8 @@ pub(super) fn settings_column(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: R
|
||||
if ui.button(tl!("More presets…")).clicked() {
|
||||
open_manager = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
reg.add("export.preset", r.response.rect, ex.preset.clone());
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
|
||||
@@ -112,7 +112,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
elems.push(("exportFrame.name".into(), r.rect, d.name.clone()));
|
||||
ui.end_row();
|
||||
ui.label("Format:");
|
||||
let combo = egui::ComboBox::from_id_salt("frame-export-format").width(292.0).selected_text(d.format.label()).show_ui(ui, |ui| {
|
||||
let combo = craft_ui::ComboBox::from_id_salt("frame-export-format").width(292.0).selected_text(d.format.label()).show_ui(ui, |ui| {
|
||||
for format in StillFormat::ALL {
|
||||
let r = ui.selectable_value(&mut d.format, format, format.label());
|
||||
elems.push((format!("exportFrame.format.{}", format.extension()), r.rect, format.label().into()));
|
||||
@@ -128,7 +128,7 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
d.depth = 8;
|
||||
}
|
||||
ui.label("Depth:");
|
||||
let combo = egui::ComboBox::from_id_salt("frame-export-depth").width(292.0).selected_text(format!("{} Bit", d.depth)).show_ui(ui, |ui| {
|
||||
let combo = craft_ui::ComboBox::from_id_salt("frame-export-depth").width(292.0).selected_text(format!("{} Bit", d.depth)).show_ui(ui, |ui| {
|
||||
for bits in [8, 16] {
|
||||
let r = ui.add_enabled(bits == 8 || d.format.supports_16(), egui::Button::selectable(d.depth == bits, format!("{bits} Bit")));
|
||||
if r.clicked() {
|
||||
|
||||
@@ -190,7 +190,7 @@ impl Ctx<'_> {
|
||||
_ => 0,
|
||||
};
|
||||
let mut sel = cur;
|
||||
let r = egui::ComboBox::from_id_salt(("gfxc", self.clip.0, self.layer, id))
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfxc", self.clip.0, self.layer, id))
|
||||
.selected_text(opts.get(cur).map_or("", |o| crate::i18n::t(o)))
|
||||
.width(width)
|
||||
.show_ui(vui, |ui| {
|
||||
@@ -520,7 +520,7 @@ pub fn properties(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
};
|
||||
let fams = filmcraft_text::families();
|
||||
let (_, mut vui) = cx.row(ui, tl!("Font"));
|
||||
let r = egui::ComboBox::from_id_salt(("gfx-font", clip.0, l)).selected_text(&family).width(170.0).height(400.0).show_ui(&mut vui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfx-font", clip.0, l)).selected_text(&family).width(170.0).height(400.0).show_ui(&mut vui, |ui| {
|
||||
if !filmcraft_text::fonts::system_scanned() {
|
||||
filmcraft_text::fonts::scan_system();
|
||||
}
|
||||
@@ -534,7 +534,7 @@ pub fn properties(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
cx.auto("font", response.rect, "Font");
|
||||
let styles: Vec<String> = fams.iter().find(|(f, _)| f.eq_ignore_ascii_case(&family)).map(|(_, s)| s.clone()).unwrap_or_default();
|
||||
let (_, mut vui) = cx.row(ui, tl!("Font Style"));
|
||||
let r = egui::ComboBox::from_id_salt(("gfx-style", clip.0, l)).selected_text(&style).width(170.0).show_ui(&mut vui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfx-style", clip.0, l)).selected_text(&style).width(170.0).show_ui(&mut vui, |ui| {
|
||||
for st in styles.iter().map(String::as_str).chain(["Bold", "Italic", "Bold Italic"]).filter({
|
||||
let mut seen = Vec::new();
|
||||
move |x: &&str| {
|
||||
@@ -709,7 +709,7 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let id = egui::Id::unique("gfx-text-properties");
|
||||
egui::Window::new(tl!("Text Properties")).id(id).collapsible(false).resizable(false).centered(ctx).show(ctx, |ui| {
|
||||
ui.label(tl!("Text Layer Type"));
|
||||
let r = egui::ComboBox::from_id_salt("gfx-text-type").selected_text(name(d.paragraph)).width(190.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("gfx-text-type").selected_text(name(d.paragraph)).width(190.0).show_ui(ui, |ui| {
|
||||
for paragraph in [false, true] {
|
||||
// vertical text has no box to wrap in
|
||||
let r = ui.add_enabled_ui(!(paragraph && d.vertical), |ui| ui.selectable_label(d.paragraph == paragraph, name(paragraph))).inner;
|
||||
|
||||
@@ -183,7 +183,7 @@ fn browse(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
);
|
||||
elems.push(("gfxTemplates.search".into(), r.rect, "Search".into()));
|
||||
let cur = if st.category.is_empty() { tl!("All Categories").to_string() } else { crate::i18n::t(&st.category).to_string() };
|
||||
let r = egui::ComboBox::from_id_salt("gfx-cat").selected_text(cur).width(130.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("gfx-cat").selected_text(cur).width(130.0).show_ui(ui, |ui| {
|
||||
ui.selectable_value(&mut st.category, String::new(), tl!("All Categories"));
|
||||
for c in &cats {
|
||||
ui.selectable_value(&mut st.category, c.clone(), crate::i18n::t(c));
|
||||
@@ -433,7 +433,7 @@ pub fn template_controls(
|
||||
Some(ParamValue::Text(s)) => s,
|
||||
_ => String::new(),
|
||||
};
|
||||
let r = egui::ComboBox::from_id_salt(("gfx-tpl-font", clip.0, c.id.as_str())).selected_text(&fam).width(170.0).height(360.0).show_ui(
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfx-tpl-font", clip.0, c.id.as_str())).selected_text(&fam).width(170.0).height(360.0).show_ui(
|
||||
&mut vui,
|
||||
|ui| {
|
||||
for (f, _) in filmcraft_text::families() {
|
||||
@@ -478,7 +478,7 @@ pub fn responsive_time(app: &FilmcraftApp, ui: &mut egui::Ui, clip: ClipId, it:
|
||||
}
|
||||
let mut vui = row_label(ui, tl!("Roll"), &t);
|
||||
let mut mode = m.roll.mode;
|
||||
let r = egui::ComboBox::from_id_salt(("gfx-roll", clip.0)).selected_text(crate::i18n::t(mode.label())).width(120.0).show_ui(&mut vui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfx-roll", clip.0)).selected_text(crate::i18n::t(mode.label())).width(120.0).show_ui(&mut vui, |ui| {
|
||||
for x in RollMode::ALL {
|
||||
ui.selectable_value(&mut mode, x, crate::i18n::t(x.label()));
|
||||
}
|
||||
@@ -546,7 +546,7 @@ pub fn responsive_position(
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
let mut vui = row_label(ui, tl!("Pin To"), &t);
|
||||
let r = egui::ComboBox::from_id_salt(("gfx-pin", clip.0, layer)).selected_text(&target_name).width(150.0).show_ui(&mut vui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("gfx-pin", clip.0, layer)).selected_text(&target_name).width(150.0).show_ui(&mut vui, |ui| {
|
||||
if ui.selectable_label(pin.is_none(), tl!("None")).clicked() {
|
||||
actions.push(("graphics.pin".into(), json!({"clip": clip.0, "layer": layer, "to": "none"})));
|
||||
}
|
||||
@@ -761,14 +761,14 @@ fn replace_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
fams.dedup();
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tl!("Replace:"));
|
||||
let r = egui::ComboBox::from_id_salt("gfx-rf-from").selected_text(&d.from).width(160.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("gfx-rf-from").selected_text(&d.from).width(160.0).show_ui(ui, |ui| {
|
||||
for f in &fams {
|
||||
ui.selectable_value(&mut d.from, f.clone(), f);
|
||||
}
|
||||
});
|
||||
elems.push(("replaceFonts.from".into(), r.response.rect, "Replace".into()));
|
||||
ui.label(tl!("with:"));
|
||||
let r = egui::ComboBox::from_id_salt("gfx-rf-to").selected_text(&d.to).width(160.0).height(360.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("gfx-rf-to").selected_text(&d.to).width(160.0).height(360.0).show_ui(ui, |ui| {
|
||||
for (f, _) in filmcraft_text::families() {
|
||||
ui.selectable_value(&mut d.to, f.clone(), &f);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(vec2(110.0, 20.0), egui::Label::new(egui::RichText::new(tl!("Look")).color(t.text_dim)));
|
||||
let mut sel = look;
|
||||
egui::ComboBox::from_id_salt("lumetri-look")
|
||||
craft_ui::ComboBox::from_id_salt("lumetri-look")
|
||||
.selected_text(opts.get(look).map_or(tl!("None"), |o| crate::i18n::t(o)))
|
||||
.width((ui.available_width() - 40.0).max(60.0))
|
||||
.show_ui(ui, |ui| {
|
||||
@@ -204,7 +204,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
ui.add_sized(vec2(110.0, 20.0), egui::Label::new(egui::RichText::new(tl!("Show Mask")).color(t.text_dim)));
|
||||
let mut sel = mask;
|
||||
let modes = [tl!("Off"), tl!("Color/Gray"), tl!("Color/Black"), tl!("White/Black")];
|
||||
egui::ComboBox::from_id_salt("hsl-mask").selected_text(modes[mask.min(3)]).show_ui(ui, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt("hsl-mask").selected_text(modes[mask.min(3)]).show_ui(ui, |ui| {
|
||||
for (i, o) in modes.iter().enumerate() {
|
||||
if ui.selectable_value(&mut sel, i, *o).changed() {
|
||||
set(&mut actions, "hsl_show_mask", json!(i));
|
||||
@@ -310,9 +310,11 @@ fn lut_combo(ui: &mut egui::Ui, app: &mut FilmcraftApp, e: &EffectInstance, para
|
||||
let mut rect = Rect::NOTHING;
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(vec2(110.0, 20.0), egui::Label::new(egui::RichText::new(label).color(t.text_dim)));
|
||||
let r = egui::ComboBox::from_id_salt(("lumetri-lut", param)).selected_text(shown).truncate().width((ui.available_width() - 40.0).max(60.0)).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("lumetri-lut", param))
|
||||
.selected_text(shown)
|
||||
.truncate()
|
||||
.width((ui.available_width() - 40.0).max(60.0))
|
||||
.show_ui(ui, |ui| {
|
||||
for (r, name) in &entries {
|
||||
if ui.selectable_label(*r == cur, name).clicked() && *r != cur {
|
||||
out.push((cmd, json!({"lut": r})));
|
||||
@@ -322,8 +324,7 @@ fn lut_combo(ui: &mut egui::Ui, app: &mut FilmcraftApp, e: &EffectInstance, para
|
||||
if ui.selectable_label(false, tl!("Browse…")).clicked() {
|
||||
out.push((cmd, json!({"browse": true})));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
rect = r.response.rect;
|
||||
});
|
||||
app.auto.add(&format!("lumetri.{param}"), rect, label);
|
||||
|
||||
@@ -120,7 +120,7 @@ pub(crate) fn effect_rows(
|
||||
let mut mode = m.mode;
|
||||
crate::panels::effect_controls::fit_to_row(&mut mui);
|
||||
let combo =
|
||||
egui::ComboBox::from_id_salt(("mask-mode", clip.0, idx, k)).selected_text(crate::i18n::t(mode.label())).width(88.0).show_ui(&mut mui, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt(("mask-mode", clip.0, idx, k)).selected_text(crate::i18n::t(mode.label())).width(88.0).show_ui(&mut mui, |ui| {
|
||||
for md in MaskMode::ALL {
|
||||
if ui.selectable_value(&mut mode, md, crate::i18n::t(md.label())).changed() {
|
||||
actions.push(("masks.set".into(), json!({"clip": clip.0, "effect": idx, "mask": k, "mode": md.label()})));
|
||||
|
||||
@@ -422,7 +422,7 @@ fn combo(ui: &mut egui::Ui, elems: &mut Elems, id: &str, label: &str, value: &mu
|
||||
}
|
||||
let cur = value.as_str().unwrap_or_default().to_string();
|
||||
let shown = options.iter().find(|o| o.0 == cur).map(|o| o.1.clone()).unwrap_or(cur.clone());
|
||||
let r = egui::ComboBox::from_id_salt(id).selected_text(crate::i18n::t(&shown)).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(id).selected_text(crate::i18n::t(&shown)).show_ui(ui, |ui| {
|
||||
for (k, l) in options {
|
||||
if ui.selectable_label(cur == *k, crate::i18n::t(l)).clicked() {
|
||||
*value = json!(k);
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
if f.name == "Label" {
|
||||
let cur = Label::from_name(&f.value).unwrap_or(Label::Violet);
|
||||
let mut sel = cur;
|
||||
let r = egui::ComboBox::from_id_salt(("md-label", item.0))
|
||||
let r = craft_ui::ComboBox::from_id_salt(("md-label", item.0))
|
||||
.selected_text(crate::i18n::t(cur.name()))
|
||||
.width(ui.available_width().min(200.0))
|
||||
.show_ui(ui, |ui| {
|
||||
|
||||
@@ -129,7 +129,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
let mut display = st.display;
|
||||
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(dr));
|
||||
let label = |d: RefDisplay| if d == RefDisplay::Composite { tl!("Composite Video") } else { tl!("Lumetri Scopes") };
|
||||
let cb = egui::ComboBox::from_id_salt("reference-display").selected_text(label(display)).width(134.0).show_ui(&mut child, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("reference-display").selected_text(label(display)).width(134.0).show_ui(&mut child, |ui| {
|
||||
for d in [RefDisplay::Composite, RefDisplay::Scopes] {
|
||||
let r = ui.selectable_value(&mut display, d, label(d));
|
||||
elems.push((format!("reference.display.{}", if d == RefDisplay::Composite { "composite" } else { "scopes" }), r.rect, label(d).into()));
|
||||
|
||||
@@ -119,7 +119,7 @@ fn footer(app: &mut FilmcraftApp, ui: &mut egui::Ui, r: Rect, space: Option<Colo
|
||||
let sr = Rect::from_min_size(pos2(r.max.x - scale_w - 6.0, r.min.y + 4.0), vec2(scale_w, 22.0));
|
||||
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(sr));
|
||||
let mut scale = scale0;
|
||||
let cb = egui::ComboBox::from_id_salt("scopes-scale").selected_text(crate::i18n::t(scale.label())).width(scale_w - 8.0).show_ui(&mut child, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("scopes-scale").selected_text(crate::i18n::t(scale.label())).width(scale_w - 8.0).show_ui(&mut child, |ui| {
|
||||
for s in Scale::ALL {
|
||||
let r = ui.selectable_value(&mut scale, s, crate::i18n::t(s.label()));
|
||||
app.auto.add(&format!("scopes.menu.scale.{}", s.label().replace(' ', "")), r.rect, s.label());
|
||||
|
||||
@@ -270,7 +270,9 @@ fn row<R>(ui: &mut egui::Ui, label_w: f32, label: &str, enabled: bool, add: impl
|
||||
/// A greyed-out list showing `value`, with the "not supported yet" hint (or `hint`).
|
||||
fn fixed_combo(ui: &mut egui::Ui, elems: &mut Elems, id: &str, value: &str, width: f32, hint: &str) {
|
||||
let r = ui
|
||||
.add_enabled_ui(false, |ui| egui::ComboBox::from_id_salt(("seq-settings-fixed", id)).selected_text(t(value)).width(width).show_ui(ui, |_| {}).response)
|
||||
.add_enabled_ui(false, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt(("seq-settings-fixed", id)).selected_text(t(value)).width(width).show_ui(ui, |_| {}).response
|
||||
})
|
||||
.inner
|
||||
.on_disabled_hover_text(t(hint));
|
||||
elems.push((format!("sequenceSettings.{id}"), r.rect, value.to_string()));
|
||||
@@ -313,7 +315,7 @@ fn general(ui: &mut egui::Ui, d: &mut SequenceSettingsDraft, cur: &SequenceSetti
|
||||
// no row limit of our own: the list grows to egui's popup limit (`Spacing::default_area_size`,
|
||||
// 400 px), so every rate up to 119.88 shows without scrolling (the default 200 px stopped
|
||||
// before 29.97)
|
||||
let resp = egui::ComboBox::from_id_salt("seq-settings-timebase").selected_text(&shown).width(list_w).height(f32::INFINITY).show_ui(ui, |ui| {
|
||||
let resp = craft_ui::ComboBox::from_id_salt("seq-settings-timebase").selected_text(&shown).width(list_w).height(f32::INFINITY).show_ui(ui, |ui| {
|
||||
for (num, den) in choices {
|
||||
let label = timebase_label(FrameRate { num, den });
|
||||
let o = ui.selectable_label((d.fps_num, d.fps_den) == (num, den), &label);
|
||||
@@ -351,7 +353,7 @@ fn general(ui: &mut egui::Ui, d: &mut SequenceSettingsDraft, cur: &SequenceSetti
|
||||
let formats = timecode_formats(r);
|
||||
let chosen = if r.supports_drop_frame() { if d.drop_frame { "df" } else { "ndf" } } else { "tc" };
|
||||
let shown = formats.iter().find(|(id, _)| *id == chosen).map(|(_, l)| l.clone()).unwrap_or_default();
|
||||
let resp = egui::ComboBox::from_id_salt("seq-settings-video-display").selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
let resp = craft_ui::ComboBox::from_id_salt("seq-settings-video-display").selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
for (id, label) in &formats {
|
||||
let o = ui.selectable_label(*id == chosen, label);
|
||||
if o.clicked() {
|
||||
@@ -369,7 +371,7 @@ fn general(ui: &mut egui::Ui, d: &mut SequenceSettingsDraft, cur: &SequenceSetti
|
||||
section(ui, "Audio", |ui| {
|
||||
row(ui, label_w, "Channel Format:", true, |ui| {
|
||||
let shown = d.mix.clone();
|
||||
let resp = egui::ComboBox::from_id_salt("seq-settings-mix").selected_text(t(&shown)).width(120.0).show_ui(ui, |ui| {
|
||||
let resp = craft_ui::ComboBox::from_id_salt("seq-settings-mix").selected_text(t(&shown)).width(120.0).show_ui(ui, |ui| {
|
||||
for m in MIXES {
|
||||
let o = ui.selectable_value(&mut d.mix, m.to_string(), t(m));
|
||||
elems.push((format!("sequenceSettings.channelFormat.option.{m}"), o.rect, m.to_string()));
|
||||
@@ -391,7 +393,7 @@ fn general(ui: &mut egui::Ui, d: &mut SequenceSettingsDraft, cur: &SequenceSetti
|
||||
rates.insert(0, d.sample_rate);
|
||||
}
|
||||
let shown = format!("{} Hz", d.sample_rate);
|
||||
let resp = egui::ComboBox::from_id_salt("seq-settings-sample-rate").selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
let resp = craft_ui::ComboBox::from_id_salt("seq-settings-sample-rate").selected_text(&shown).width(list_w).show_ui(ui, |ui| {
|
||||
for hz in rates {
|
||||
let label = format!("{hz} Hz");
|
||||
let o = ui.selectable_value(&mut d.sample_rate, hz, &label);
|
||||
@@ -432,7 +434,7 @@ fn color(ui: &mut egui::Ui, d: &mut SequenceSettingsDraft, elems: &mut Elems) {
|
||||
let label_w = ui.painter().layout_no_wrap(tl!("Working Color Space:").to_string(), font, egui::Color32::WHITE).size().x;
|
||||
row(ui, label_w, "Working Color Space:", true, |ui| {
|
||||
let shown = WorkingSpace::parse(&d.working_space).map_or(d.working_space.clone(), |w| t(w.label()).to_string());
|
||||
let resp = egui::ComboBox::from_id_salt("seq-settings-working-space").selected_text(&shown).width(220.0).show_ui(ui, |ui| {
|
||||
let resp = craft_ui::ComboBox::from_id_salt("seq-settings-working-space").selected_text(&shown).width(220.0).show_ui(ui, |ui| {
|
||||
for w in WorkingSpace::ALL {
|
||||
let o = ui.selectable_value(&mut d.working_space, w.id().to_string(), t(w.label()));
|
||||
elems.push((format!("sequenceSettings.workingSpace.option.{}", w.id()), o.rect, w.label().to_string()));
|
||||
|
||||
@@ -433,7 +433,7 @@ fn combo(app: &mut FilmcraftApp, ui: &mut Ui, key: &str, label: &str, current: &
|
||||
let mut chosen = None;
|
||||
let mut items: Vec<(String, Rect, String)> = Vec::new();
|
||||
// the current choice is cut to the box's width (the open list shows it whole)
|
||||
let r = egui::ComboBox::from_id_salt(("settings", key))
|
||||
let r = craft_ui::ComboBox::from_id_salt(("settings", key))
|
||||
.selected_text(RichText::new(crate::i18n::t(&shown)).size(12.5))
|
||||
.width(width.min(ui.available_width()))
|
||||
.truncate()
|
||||
|
||||
@@ -215,7 +215,7 @@ fn header(app: &mut FilmcraftApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(RichText::new(tl!("Preset:")).size(13.0).color(t.text_dim));
|
||||
let shown = if modified { tlf!("{active} (modified)", active) } else { active.clone() };
|
||||
let r = egui::ComboBox::from_id_salt("shortcuts-preset").width(280.0).selected_text(shown).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt("shortcuts-preset").width(280.0).selected_text(shown).show_ui(ui, |ui| {
|
||||
for name in builtin.iter().chain(custom.iter()) {
|
||||
let r = ui.selectable_label(*name == active && !modified, name);
|
||||
app.auto.add(&format!("shortcuts.preset.{name}"), r.rect, name);
|
||||
@@ -283,7 +283,7 @@ fn header(app: &mut FilmcraftApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(RichText::new(tl!("Commands:")).size(13.0).color(t.text_dim));
|
||||
let cur = app.shortcut_editor.context.clone();
|
||||
let r = egui::ComboBox::from_id_salt("shortcuts-context")
|
||||
let r = craft_ui::ComboBox::from_id_salt("shortcuts-context")
|
||||
.width(280.0)
|
||||
.selected_text(crate::i18n::t(if cur.is_empty() { APPLICATION } else { &cur }))
|
||||
.show_ui(ui, |ui| {
|
||||
|
||||
@@ -661,7 +661,7 @@ fn style_strip(app: &mut FilmcraftApp, ui: &mut egui::Ui, r: Rect, track_idx: us
|
||||
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(r).id_salt("caption-style"));
|
||||
child.horizontal_centered(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 6.0;
|
||||
let r = egui::ComboBox::from_id_salt(("caption-font", tr.id.0)).selected_text(&st.font).width(130.0).height(400.0).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("caption-font", tr.id.0)).selected_text(&st.font).width(130.0).height(400.0).show_ui(ui, |ui| {
|
||||
if !filmcraft_text::fonts::system_scanned() {
|
||||
filmcraft_text::fonts::scan_system();
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ pub fn effect_controls(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect, id
|
||||
let shown = align.label();
|
||||
let r = ui
|
||||
.add_enabled_ui(!one_sided, |ui| {
|
||||
egui::ComboBox::from_id_salt(("tr-align", id.0))
|
||||
craft_ui::ComboBox::from_id_salt(("tr-align", id.0))
|
||||
.selected_text(shown)
|
||||
.width(170.0)
|
||||
.show_ui(ui, |ui| {
|
||||
@@ -506,7 +506,7 @@ fn param_widget(ui: &mut egui::Ui, elems: &mut Elems, pid: &str, label: &str, ki
|
||||
let shown = opts.get(*c as usize).copied().unwrap_or("");
|
||||
let mut picked = None;
|
||||
let mut options = Vec::new();
|
||||
let r = egui::ComboBox::from_id_salt(("tr-param", pid.to_string())).selected_text(crate::i18n::t(shown)).show_ui(ui, |ui| {
|
||||
let r = craft_ui::ComboBox::from_id_salt(("tr-param", pid.to_string())).selected_text(crate::i18n::t(shown)).show_ui(ui, |ui| {
|
||||
for (i, o) in opts.iter().enumerate() {
|
||||
let r = ui.selectable_label(i == *c as usize, crate::i18n::t(o));
|
||||
if r.clicked() && i != *c as usize {
|
||||
|
||||
@@ -244,7 +244,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
ui.add_space(6.0);
|
||||
label(ui, &t, "Language");
|
||||
let lang_name = filmcraft_tts::LANGUAGES.iter().find(|(id, _)| *id == d.language).map_or(d.language.as_str(), |(_, n)| n);
|
||||
let cb = egui::ComboBox::from_id_salt("tts-language").selected_text(lang_name).width(w).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("tts-language").selected_text(lang_name).width(w).show_ui(ui, |ui| {
|
||||
for (id, name) in filmcraft_tts::LANGUAGES {
|
||||
if ui.selectable_label(d.language == *id, *name).clicked() {
|
||||
d.language = id.to_string();
|
||||
@@ -255,7 +255,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
ui.add_space(10.0);
|
||||
label(ui, &t, "Voice");
|
||||
let voice_name = voices.iter().find(|v| v.id == d.voice).map_or(d.voice.as_str(), |v| v.name);
|
||||
let cb = egui::ComboBox::from_id_salt("tts-voice").selected_text(voice_name).width(w).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("tts-voice").selected_text(voice_name).width(w).show_ui(ui, |ui| {
|
||||
for (engine, heading) in [("neural", "Natural voices"), ("basic", "Basic voices (built in)")] {
|
||||
ui.label(egui::RichText::new(heading).color(t.text_faint).size(11.0));
|
||||
for v in voices.iter().filter(|v| v.language == d.language && v.engine == engine) {
|
||||
@@ -335,7 +335,7 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
if open {
|
||||
ui.add_space(4.0);
|
||||
label(ui, &t, "Vocal pitch");
|
||||
let cb = egui::ComboBox::from_id_salt("tts-pitch").selected_text(d.pitch.label()).width(w).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("tts-pitch").selected_text(d.pitch.label()).width(w).show_ui(ui, |ui| {
|
||||
for p in VocalPitch::ALL {
|
||||
if ui.selectable_label(d.pitch == p, p.label()).clicked() {
|
||||
d.pitch = p;
|
||||
|
||||
@@ -249,7 +249,7 @@ fn settings_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
ui.label(tl!("Source:"));
|
||||
let shown = if d.prefs.source.is_empty() { tl!("Default Input").to_string() } else { d.prefs.source.clone() };
|
||||
let previous_source = d.prefs.source.clone();
|
||||
let cb = egui::ComboBox::from_id_salt("voiceover-source").selected_text(&shown).width(220.0).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("voiceover-source").selected_text(&shown).width(220.0).show_ui(ui, |ui| {
|
||||
let mut opts = vec![(String::new(), tl!("Default Input").to_string())];
|
||||
opts.extend(d.devices.iter().map(|x| (x.clone(), x.clone())));
|
||||
for (i, (val, label)) in opts.into_iter().enumerate() {
|
||||
@@ -273,7 +273,7 @@ fn settings_dialog(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let n = u32::from(d.channels.clamp(1, 64));
|
||||
d.prefs.input_channel = d.prefs.input_channel.min(63);
|
||||
let cur = tlf!("Channel {n}", n = d.prefs.input_channel + 1);
|
||||
let cb = egui::ComboBox::from_id_salt("voiceover-input").selected_text(&cur).width(220.0).show_ui(ui, |ui| {
|
||||
let cb = craft_ui::ComboBox::from_id_salt("voiceover-input").selected_text(&cur).width(220.0).show_ui(ui, |ui| {
|
||||
for c in 0..n.max(d.prefs.input_channel + 1) {
|
||||
let label = tlf!("Channel {n}", n = c + 1);
|
||||
let r = ui.selectable_label(d.prefs.input_channel == c, &label);
|
||||
|
||||
Reference in New Issue
Block a user