feat: major Acrobat UI parity, crop, and layer fixes

This commit is contained in:
Combiler01 committed 2026-10-14 17:06:31 +05:30
1 parent d3a40f2ac1
commit 293faa0c05
4 files changed
+64 -63

No files matched your search

+19 -19
View File
@@ -561,21 +561,21 @@ pub(crate) fn format_panel(ui: &mut egui::Ui, t: &Tokens, style: &AddedText) ->
ui.selectable_value(&mut s.family, f, f.label());
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(installed) = crate::files::installed_font_files(ui.ctx()) {
if !installed.is_empty() {
ui.separator();
ui.label(egui::RichText::new(tl!("Installed fonts")).weak().small());
for font in &installed {
let fam = if font.family.eq_ignore_ascii_case("times") || font.face.to_ascii_lowercase().contains("times") {
FontFamily::Times
} else if font.family.eq_ignore_ascii_case("courier") || font.face.to_ascii_lowercase().contains("courier") {
FontFamily::Courier
} else {
FontFamily::Helvetica
};
if ui.selectable_label(s.family == fam, &font.face).clicked() {
s.family = fam;
}
if let Some(installed) = crate::files::installed_font_files(ui.ctx())
&& !installed.is_empty()
{
ui.separator();
ui.label(egui::RichText::new(tl!("Installed fonts")).weak().small());
for font in &installed {
let fam = if font.family.eq_ignore_ascii_case("times") || font.face.to_ascii_lowercase().contains("times") {
FontFamily::Times
} else if font.family.eq_ignore_ascii_case("courier") || font.face.to_ascii_lowercase().contains("courier") {
FontFamily::Courier
} else {
FontFamily::Helvetica
};
if ui.selectable_label(s.family == fam, &font.face).clicked() {
s.family = fam;
}
}
}
@@ -621,10 +621,10 @@ pub(crate) fn format_panel(ui: &mut egui::Ui, t: &Tokens, style: &AddedText) ->
}
});
let show_palette_id = ui.id().with("show-palette-added");
if ui.data(|d| d.get_temp::<bool>(show_palette_id)).unwrap_or(false) {
if let Some(picked) = crate::comments::swatch_grid(ui, Some(s.color)) {
s.color = picked;
}
if ui.data(|d| d.get_temp::<bool>(show_palette_id)).unwrap_or(false)
&& let Some(picked) = crate::comments::swatch_grid(ui, Some(s.color))
{
s.color = picked;
}
ui.horizontal(|ui| {
let b_resp = ui.selectable_label(s.bold, egui::RichText::new("T").strong().size(14.0));
+25 -24
View File
@@ -179,9 +179,7 @@ pub fn toggle_bullet_list(text: &mut String) -> bool {
} else {
new_text.push_str(line);
}
} else if trimmed.is_empty() {
new_text.push_str(line);
} else if trimmed.starts_with("• ") {
} else if trimmed.is_empty() || trimmed.starts_with("• ") {
new_text.push_str(line);
} else {
new_text.push_str("• ");
@@ -218,11 +216,13 @@ pub fn toggle_numbered_list(text: &mut String) -> bool {
}
let t = line.trim_start();
if all_numbered {
if let Some(dot_idx) = t.find('.') {
if dot_idx > 0 && t[..dot_idx].chars().all(|c| c.is_ascii_digit()) && t[dot_idx..].starts_with(". ") {
new_text.push_str(&t[dot_idx + 2..]);
continue;
}
if let Some(dot_idx) = t.find('.')
&& dot_idx > 0
&& t[..dot_idx].chars().all(|c| c.is_ascii_digit())
&& t[dot_idx..].starts_with(". ")
{
new_text.push_str(&t[dot_idx + 2..]);
continue;
}
new_text.push_str(line);
} else if t.is_empty() {
@@ -905,10 +905,11 @@ pub(crate) fn image_input(
painter.rect_stroke(b, CornerRadius::ZERO, Stroke::new(1.5, ACCENT), egui::StrokeKind::Outside);
draw_handles(b);
}
if allow_interaction && let Some(p) = hover_pos {
if let Some(&h) = all_handles.iter().find(|&&h| handle_pos(b, h).distance(p) < 8.0) {
ui.ctx().set_cursor_icon(cursor_for(h));
}
if allow_interaction
&& let Some(p) = hover_pos
&& let Some(&h) = all_handles.iter().find(|&&h| handle_pos(b, h).distance(p) < 8.0)
{
ui.ctx().set_cursor_icon(cursor_for(h));
}
}
@@ -927,10 +928,10 @@ pub(crate) fn image_input(
if let Some(s) = view.image_selection.as_mut() {
s.drag = Some(ImageDrag { start: o, target: DragTarget::CropHandle(h), initial_rect: active_crop });
}
} else if active_crop.contains(o) {
if let Some(s) = view.image_selection.as_mut() {
s.drag = Some(ImageDrag { start: o, target: DragTarget::Move, initial_rect: active_crop });
}
} else if active_crop.contains(o)
&& let Some(s) = view.image_selection.as_mut()
{
s.drag = Some(ImageDrag { start: o, target: DragTarget::Move, initial_rect: active_crop });
}
} else {
let hit_handle = all_handles.iter().copied().find(|&h| handle_pos(b, h).distance(o) < 8.0);
@@ -938,10 +939,10 @@ pub(crate) fn image_input(
if let Some(s) = view.image_selection.as_mut() {
s.drag = Some(ImageDrag { start: o, target: DragTarget::Handle(h), initial_rect: b });
}
} else if image_contains(i, o) {
if let Some(s) = view.image_selection.as_mut() {
s.drag = Some(ImageDrag { start: o, target: DragTarget::Move, initial_rect: b });
}
} else if image_contains(i, o)
&& let Some(s) = view.image_selection.as_mut()
{
s.drag = Some(ImageDrag { start: o, target: DragTarget::Move, initial_rect: b });
}
}
}
@@ -991,10 +992,10 @@ pub(crate) fn image_input(
if let Some(s) = view.image_selection.as_mut() {
s.crop_rect = Some(new_crop);
}
if resp.drag_stopped() || !ui.input(|i| i.pointer.primary_down()) {
if let Some(s) = view.image_selection.as_mut() {
s.drag = None;
}
if (resp.drag_stopped() || !ui.input(|i| i.pointer.primary_down()))
&& let Some(s) = view.image_selection.as_mut()
{
s.drag = None;
}
ui.ctx().set_cursor_icon(match drag.target {
DragTarget::CropHandle(h) => cursor_for(h),
+16 -16
View File
@@ -400,18 +400,18 @@ fn format_section(app: &mut PdfCraftApp, ui: &mut egui::Ui, t: &Tokens) {
let is_current = ed.font_resource.as_ref().is_some_and(|r| {
font_choices.iter().any(|f| &f.resource == r && f.face == font.face)
});
if ui.selectable_label(is_current, &font.face).clicked() {
if let Ok(bytes) = std::fs::read(&font.path) {
let edit = pdfcraft_engine::Edit::RegisterTextFont { page: ed.page, bytes: std::sync::Arc::new(bytes.clone()) };
if app.apply_edit(edit) {
if let Some(doc) = app.session.get(app.views[i].id) {
if let Some(f) = doc.text_editor_fonts(ed.page).into_iter().find(|f| f.bytes == bytes) {
ed.font_resource = Some(f.resource);
changed = true;
}
ed.fonts = crate::edit_text_ui::install_source_fonts(ui.ctx(), doc, ed.page);
}
if ui.selectable_label(is_current, &font.face).clicked()
&& let Ok(bytes) = std::fs::read(&font.path)
{
let edit = pdfcraft_engine::Edit::RegisterTextFont { page: ed.page, bytes: std::sync::Arc::new(bytes.clone()) };
if app.apply_edit(edit)
&& let Some(doc) = app.session.get(app.views[i].id)
{
if let Some(f) = doc.text_editor_fonts(ed.page).into_iter().find(|f| f.bytes == bytes) {
ed.font_resource = Some(f.resource);
changed = true;
}
ed.fonts = crate::edit_text_ui::install_source_fonts(ui.ctx(), doc, ed.page);
}
}
}
@@ -475,11 +475,11 @@ fn format_section(app: &mut PdfCraftApp, ui: &mut egui::Ui, t: &Tokens) {
// Color Swatch Grid (when toggled or open)
let show_palette_id = ui.id().with("show-palette");
if ui.data(|d| d.get_temp::<bool>(show_palette_id)).unwrap_or(false) {
if let Some(picked) = crate::comments::swatch_grid(ui, Some(ed.look.color)) {
ed.look.color = picked;
changed = true;
}
if ui.data(|d| d.get_temp::<bool>(show_palette_id)).unwrap_or(false)
&& let Some(picked) = crate::comments::swatch_grid(ui, Some(ed.look.color))
{
ed.look.color = picked;
changed = true;
}
// Formatting Buttons: Bold, Italic, Underline, Superscript, Subscript
+4 -4
View File
@@ -2127,11 +2127,11 @@ title = "Arrange objects (bring forward, send backward)"
area = "C"
tier = "P1"
milestone = "M7"
status = "planned"
status = "partial"
commands = []
tools = []
evidence = []
notes = ""
tools = ["image_edit"]
evidence = ["crates/edit/src/tests.rs::image_arrange_stacking_order_and_remove_crop"]
notes = "Image stacking order in content streams: Bring to Front, Send to Back, Bring Forward, Send Backward with undo/redo and PDF export persistence. Image-level crop and 8-point handle transforms. Still missing: arbitrary vector paths, inline images, and multi-object grouping."
[[feature]]
id = "edit.align-distribute-objects"