mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 15:48:33 +00:00
Merge pull request #2645 from storytold/fix/2075-stroke-width-control
Character panel: the stroke width shows its icon and a presets menu (#2075)
This commit is contained in:
3 files changed
+58
-9
No files matched your search
@@ -112,6 +112,8 @@ pub enum Icon {
|
||||
Fullscreen,
|
||||
Channel,
|
||||
Exposure,
|
||||
/// Stroke width: three bars, thin to thick.
|
||||
StrokeWidth,
|
||||
// links
|
||||
Chat,
|
||||
Globe,
|
||||
@@ -548,6 +550,11 @@ pub fn paint(painter: &Painter, rect: Rect, icon: Icon, color: Color32) {
|
||||
}
|
||||
pen.circle(8.0, 8.0, 4.2);
|
||||
}
|
||||
StrokeWidth => {
|
||||
pen.rect_fill(2.5, 3.0, 13.5, 3.75);
|
||||
pen.rect_fill(2.5, 6.5, 13.5, 8.0);
|
||||
pen.rect_fill(2.5, 10.5, 13.5, 13.0);
|
||||
}
|
||||
Hamburger => {
|
||||
for y in [4.5, 8.0, 11.5] {
|
||||
pen.line(&[(3.0, y), (13.0, y)]);
|
||||
|
||||
@@ -127,16 +127,21 @@ fn numeric_fields(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter
|
||||
let row = i / 2;
|
||||
let fx = x0 + col as f32 * (w / 2.0);
|
||||
let fy = y + row as f32 * 28.0;
|
||||
p.text(pos2(fx, fy + 9.0), Align2::LEFT_CENTER, glyph, Tokens::semibold(11.0), t.text_dim);
|
||||
let (r, nv, _) = widgets::hot_number_at(
|
||||
ui,
|
||||
pos2(fx + 26.0, fy),
|
||||
egui::Id::unique(("char", key)),
|
||||
v,
|
||||
&widgets::HotSpec { speed: 0.5, range, decimals: 0, suffix },
|
||||
&t,
|
||||
);
|
||||
let stroke = key == "strokeWidth";
|
||||
if stroke {
|
||||
// The stroke-width icon, as in After Effects, not a letter that reads as "width".
|
||||
crate::icons::paint(p, Rect::from_min_size(pos2(fx, fy + 1.0), vec2(18.0, 18.0)), crate::icons::Icon::StrokeWidth, t.text_dim);
|
||||
} else {
|
||||
p.text(pos2(fx, fy + 9.0), Align2::LEFT_CENTER, glyph, Tokens::semibold(11.0), t.text_dim);
|
||||
}
|
||||
// Fractional stroke widths (the 0.25 / 0.5 / 0.75 presets) show their decimals.
|
||||
let decimals = if stroke && v.fract().abs() > 1e-9 { 2 } else { 0 };
|
||||
let (r, nv, _) =
|
||||
widgets::hot_number_at(ui, pos2(fx + 26.0, fy), egui::Id::unique(("char", key)), v, &widgets::HotSpec { speed: 0.5, range, decimals, suffix }, &t);
|
||||
app.auto.add(&format!("character.{key}"), r, key);
|
||||
if stroke {
|
||||
stroke_width_presets(app, ui, r, enabled, actions);
|
||||
}
|
||||
if let Some(nv) = nv
|
||||
&& enabled
|
||||
{
|
||||
@@ -145,6 +150,26 @@ fn numeric_fields(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter
|
||||
}
|
||||
}
|
||||
|
||||
/// The stroke width's presets menu beside its value (`r`), as in After Effects.
|
||||
fn stroke_width_presets(app: &mut EffectcraftApp, ui: &mut egui::Ui, r: Rect, enabled: bool, actions: &mut Vec<serde_json::Value>) {
|
||||
const PRESETS: [f64; 22] = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0];
|
||||
let t = app.tokens;
|
||||
let br = Rect::from_min_size(pos2(r.max.x + 4.0, r.min.y + 2.0), vec2(16.0, 16.0));
|
||||
let pop = egui::Id::unique("char-stroke-width-pop");
|
||||
if widgets::icon_button(ui, br, crate::icons::Icon::ChevronDown, false, &t, egui::Id::unique("char-stroke-width-presets"))
|
||||
.on_hover_text(tr("Stroke width presets"))
|
||||
.clicked()
|
||||
&& enabled
|
||||
{
|
||||
widgets::open_popup(ui, pop);
|
||||
}
|
||||
app.auto.add("character.strokeWidth.presets", br, "Stroke width presets");
|
||||
let labels: Vec<String> = PRESETS.iter().map(|v| format!("{v} px")).collect();
|
||||
if let Some(v) = widgets::popup_menu(ui, pop, br.left_bottom(), &labels, None).and_then(|i| PRESETS.get(i)) {
|
||||
actions.push(json!({"strokeWidth": v}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Kerning: Metrics / Optical / a manual value (grid row 1, left).
|
||||
fn kerning_field(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, g: Grid, doc: &TextDoc, actions: &mut Vec<serde_json::Value>) {
|
||||
let t = app.tokens;
|
||||
|
||||
@@ -8,6 +8,7 @@ use effectcraft_ui_egui::EffectcraftApp;
|
||||
use effectcraft_ui_egui::dock::PanelKind;
|
||||
use egui::{Event, Key, Modifiers, PointerButton, Pos2};
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
use serde_json::json;
|
||||
|
||||
fn app() -> EffectcraftApp {
|
||||
@@ -1854,3 +1855,19 @@ fn character_panel_line_join_buttons_set_the_join() {
|
||||
click(&mut h, bevel, Modifiers::NONE);
|
||||
assert_eq!(join(&h), effectcraft_engine::keyframe::LineJoin::Bevel);
|
||||
}
|
||||
|
||||
/// #2075: the Character panel's stroke width has a presets menu (0.25 … 100 px) beside its value.
|
||||
#[test]
|
||||
fn character_panel_stroke_width_presets_set_the_width() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"name": "Type", "width": 640, "height": 360, "duration": 2})).unwrap();
|
||||
let lid = s.execute("layer.newText", json!({"text": "Stroke", "size": 80, "applyStroke": true, "strokeWidth": 8})).unwrap()["layer"].as_u64().unwrap();
|
||||
let mut h = harness(EffectcraftApp::new(s));
|
||||
h.state_mut().show_panel(PanelKind::Character);
|
||||
h.run_steps(3);
|
||||
let arrow = rect(&h, "character.strokeWidth.presets").center();
|
||||
click(&mut h, arrow, Modifiers::NONE);
|
||||
let entry = h.get_by_label("0.5 px").rect().center();
|
||||
click(&mut h, entry, Modifiers::NONE);
|
||||
assert_eq!(layer_doc(&h.state().session, LayerId(lid)).unwrap().stroke_width, 0.5);
|
||||
}
|
||||
Reference in new issue
Block a user