mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Merge pull request #373 from midasdf/fix/type-button-vertical-flyout
M7: expose Vertical Type in the basic Type tool flyout # Conflicts: # ROADMAP.md
This commit is contained in:
commit
6b294c28b2
2 files changed
+42
-4
No files matched your search
+1
-1
@@ -125,7 +125,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
|
||||
- Pathfinder (10 exact curve booleans), Offset, Outline Stroke, Simplify, Clean Up, Split Into Grid, Divide Objects Below. Object › Path › Remove Anchor Points (`path.removeAnchors`) removes the direct-selected anchors without opening their paths (the Delete key still deletes them with their segments): the neighbours keep their handle directions and their facing handles are refitted so one cubic follows the two old segments, and straight sides stay straight. The Delete Anchor Point tool uses the same refit. The Control bar, the contextual task bar, the Properties quick actions and the canvas context menu offer it when anchors are selected.
|
||||
- Live effects with previewing dialogs: Distort & Transform, Path, Convert to Shape, 15 Warp styles, Round Corners, Scribble, Effect → Pathfinder (all 10 operations, live on groups; several loose objects are grouped first, in one undo step), Color Adjustments (Brightness/Contrast, Curves, Levels, Hue/Saturation, Shift to Color, Temperature/Tint, on vectors, live type and embedded images), and raster drop shadow, glows and feather. SVG and PDF export keep live effects (geometry baked; SVG raster effects as filters).
|
||||
- **Type:** Text Wrap, Type on a Path effects (Rainbow/Skew/3D Ribbon/Stair Step/Gravity), Character and Paragraph Styles (override-preserving redefine), Area Type Options (rows/columns/inset/first baseline), threaded text across any closed shapes, Fit Headline. Area type resizes like Illustrator's type areas: a bounding-box handle drag (Selection tool) or Area Type Options Width/Height size the frame and the text reflows at its size (threads too); Direct Selection drags a frame corner or edge (`text.reshapeArea`). Point type still scales. Variable fonts list every named instance as a style of their family (Figtree Light to Black and their italics, Bahnschrift, Segoe UI Variable), and the instance chosen is shaped, drawn, outlined and measured at its axis settings; PDF and SVG embed it as that instance. Variation axis sliders are not done.
|
||||
- **Interface languages, vertical type:** VectorCraft ▸ Language (or Preferences ▸ User Interface) picks Automatic (the system locale) or a registered language, persisted as `interfaceLanguage`. Translations come from per-language catalogs (`i18n/*.tsv`): Traditional Chinese and Japanese cover the whole interface, Czech every menu label (tested); untranslated text stays English. Japanese and Chinese glyphs come from the craft-fonts build input (release builds) or the installed fonts, Czech ones from the bundled UI fonts. Vertical Type, Vertical Area Type and Vertical Type on a Path create vertical text (columns right to left, upright CJK glyphs and marks with `vert` alternates, Latin and longer numbers on their side, two- and three-digit numbers set as tate-chu-yoko in one em of the column (kept whole when justified), upright glyphs centred on the column (tracked or justified too), point type's anchor on the first column's centre line); kinsoku (the strict set) applies to horizontal and vertical Japanese, and justified Japanese lines spread their room between characters (not inside Latin words); Paragraph › Mojikumi: Line-end Punctuation Half Width (new type's default) shares one half-em space between consecutive punctuation, sets a closing mark ending a line half width, and puts a quarter em between Japanese and Latin letters or digits; Type ▸ Type Orientation switches existing text; caret, selection, hit testing and arrow keys follow the writing direction. Vertical text exports to PDF as real text and to SVG as outlines.
|
||||
- **Interface languages, vertical type:** VectorCraft ▸ Language (or Preferences ▸ User Interface) picks Automatic (the system locale) or a registered language, persisted as `interfaceLanguage`. Translations come from per-language catalogs (`i18n/*.tsv`): Traditional Chinese and Japanese cover the whole interface, Czech every menu label (tested); untranslated text stays English. Japanese and Chinese glyphs come from the craft-fonts build input (release builds) or the installed fonts, Czech ones from the bundled UI fonts. The Basic toolbar's Type button holds Vertical Type in its flyout. Vertical Type, Vertical Area Type and Vertical Type on a Path create vertical text (columns right to left, upright CJK glyphs and marks with `vert` alternates, Latin and longer numbers on their side, two- and three-digit numbers set as tate-chu-yoko in one em of the column (kept whole when justified), upright glyphs centred on the column (tracked or justified too), point type's anchor on the first column's centre line); kinsoku (the strict set) applies to horizontal and vertical Japanese, and justified Japanese lines spread their room between characters (not inside Latin words); Paragraph › Mojikumi: Line-end Punctuation Half Width (new type's default) shares one half-em space between consecutive punctuation, sets a closing mark ending a line half width, and puts a quarter em between Japanese and Latin letters or digits; Type ▸ Type Orientation switches existing text; caret, selection, hit testing and arrow keys follow the writing direction. Vertical text exports to PDF as real text and to SVG as outlines.
|
||||
- **Transparency:** opacity masks (clip/invert/disable/link), exported as SVG `<mask>` and PDF soft masks.
|
||||
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type (choosing a perspective tool shows the grid; Hide Grid hides it with the tool still chosen), Gradient Mesh, Shape Builder, Live Paint (every path an edge, open lines included), Image Trace (12 presets), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror).
|
||||
- **Paint and appearance (M3):**
|
||||
|
||||
@@ -43,7 +43,7 @@ pub const BASIC: &[(&str, &[&[&str]])] = &[
|
||||
&["shapeBuilder", "livePaintBucket", "livePaintSelection", "blend"],
|
||||
],
|
||||
),
|
||||
("Type", &[&["areaType", "typeOnPath", "verticalType", "verticalAreaType", "verticalTypeOnPath"], &["type", "touchType"]]),
|
||||
("Type", &[&["areaType", "typeOnPath", "verticalAreaType", "verticalTypeOnPath"], &["type", "verticalType", "touchType"]]),
|
||||
("Navigate", &[&["zoom"], &["hand", "printTiling"], &["rotateView"]]),
|
||||
("Color", &[&["gradient", "mesh"], &["eyedropper", "measure"]]),
|
||||
];
|
||||
@@ -167,8 +167,17 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
Stroke::NONE,
|
||||
));
|
||||
}
|
||||
let long_press = resp.is_pointer_button_down_on()
|
||||
&& ui.input(|inp| inp.pointer.press_start_time().is_some_and(|s| inp.time - s > 0.35));
|
||||
let held_for = resp
|
||||
.is_pointer_button_down_on()
|
||||
.then(|| ui.input(|inp| inp.pointer.press_start_time().map(|s| inp.time - s)))
|
||||
.flatten();
|
||||
if slot.len() > 1
|
||||
&& let Some(seconds) = held_for
|
||||
&& seconds < 0.35
|
||||
{
|
||||
ui.ctx().request_repaint_after(std::time::Duration::from_secs_f64(0.35 - seconds));
|
||||
}
|
||||
let long_press = held_for.is_some_and(|seconds| seconds >= 0.35);
|
||||
let alt = ui.input(|inp| inp.modifiers.alt);
|
||||
if (resp.secondary_clicked() || long_press) && slot.len() > 1 {
|
||||
open_flyout = Some((slot.clone(), rect));
|
||||
@@ -566,4 +575,33 @@ pub(crate) mod tests {
|
||||
frame(&mut app, &ctx, 4.05, vec![c(false)]);
|
||||
assert_eq!((app.session.tool_id(), app.ui.flyout), ("roundedRectangle", None));
|
||||
}
|
||||
#[test]
|
||||
fn type_button_long_press_selects_vertical_type_in_both_layouts() {
|
||||
for advanced in [false, true] {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.ui.toolbar_advanced = advanced;
|
||||
let all = slots(&app);
|
||||
let index = all.iter().position(|(_, tools)| tools.contains(&"type")).unwrap();
|
||||
assert!(all[index].1.contains(&"verticalType"));
|
||||
for id in ["type", "verticalType", "areaType", "verticalAreaType", "typeOnPath", "verticalTypeOnPath"] {
|
||||
assert_eq!(all.iter().filter(|(_, tools)| tools.contains(&id)).count(), 1);
|
||||
}
|
||||
let ctx = egui::Context::default();
|
||||
let at = frame_in(&mut app, &ctx, 0.0, vec![], 2000.0)[index].center();
|
||||
let pointer = |pos, pressed| Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame_in(&mut app, &ctx, 1.0, vec![Event::PointerMoved(at), pointer(at, true)], 2000.0);
|
||||
frame_in(&mut app, &ctx, 1.36, vec![], 2000.0);
|
||||
frame_in(&mut app, &ctx, 1.4, vec![pointer(at, false)], 2000.0);
|
||||
frame_in(&mut app, &ctx, 1.45, vec![], 2000.0);
|
||||
assert_eq!(app.session.tool_id(), "selection");
|
||||
let tools: Vec<String> = ctx.data(|d| d.get_temp(egui::Id::new("flyout-tools"))).unwrap();
|
||||
let row_index = tools.iter().position(|id| id == "verticalType").unwrap();
|
||||
let menu = ctx.memory(|m| m.area_rect(egui::Id::new("tool-flyout"))).unwrap();
|
||||
let row = egui::pos2(menu.left() + 60.0, menu.top() + 15.0 + row_index as f32 * 30.0);
|
||||
frame_in(&mut app, &ctx, 2.0, vec![Event::PointerMoved(row), pointer(row, true)], 2000.0);
|
||||
frame_in(&mut app, &ctx, 2.05, vec![pointer(row, false)], 2000.0);
|
||||
assert_eq!(app.session.tool_id(), "verticalType");
|
||||
assert_eq!(app.ui.flyout, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user