mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
fix(pages): Delete deletes the page picked in the Pages list (#859)
Clicking a page row (or the add-page button) gives the Pages list the keyboard: Delete or Backspace with no layers selected now deletes the current page instead of failing with "nothing is selected". No confirmation; a "Page deleted" toast says so and Undo brings it back. The context menu's Delete page shows the same toast. Selecting layers, clicking a layer row or pressing on the canvas gives Delete back to the layers.
This commit is contained in:
@@ -929,6 +929,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "Nastavení tisku PDF"
|
||||
"Padding" = "Vnitřní odsazení"
|
||||
"Page" = "Stránka"
|
||||
"Page deleted" = "Stránka smazána"
|
||||
"Pages" = "Stránky"
|
||||
"Pages added to the document" = "Stránky přidány do dokumentu"
|
||||
"Placed on the current page" = "Umístěno na aktuální stránku"
|
||||
|
||||
@@ -898,6 +898,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF-Druckeinstellungen"
|
||||
"Padding" = "Innenabstand"
|
||||
"Page" = "Seite"
|
||||
"Page deleted" = "Seite gelöscht"
|
||||
"Pages" = "Seiten"
|
||||
"Pages added to the document" = "Seiten zum Dokument hinzugefügt"
|
||||
"Placed on the current page" = "Auf der aktuellen Seite platziert"
|
||||
|
||||
@@ -950,6 +950,7 @@ name = "English"
|
||||
"PDF print settings" = "PDF print settings"
|
||||
"Padding" = "Padding"
|
||||
"Page" = "Page"
|
||||
"Page deleted" = "Page deleted"
|
||||
"Pages" = "Pages"
|
||||
"Pages added to the document" = "Pages added to the document"
|
||||
"Placed on the current page" = "Placed on the current page"
|
||||
|
||||
@@ -951,6 +951,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "Ajustes de impresión PDF"
|
||||
"Padding" = "Margen interno"
|
||||
"Page" = "Página"
|
||||
"Page deleted" = "Página eliminada"
|
||||
"Pages" = "Páginas"
|
||||
"Pages added to the document" = "Páginas añadidas al documento"
|
||||
"Placed on the current page" = "Colocado en la página actual"
|
||||
|
||||
@@ -952,6 +952,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF-tulostusasetukset"
|
||||
"Padding" = "Sisämarginaali"
|
||||
"Page" = "Sivu"
|
||||
"Page deleted" = "Sivu poistettu"
|
||||
"Pages" = "Sivut"
|
||||
"Pages added to the document" = "Sivut lisätty asiakirjaan"
|
||||
"Pages: {pages}" = "Sivut: {pages}"
|
||||
|
||||
@@ -947,6 +947,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "Réglages d’impression PDF"
|
||||
"Padding" = "Marge intérieure"
|
||||
"Page" = "Page"
|
||||
"Page deleted" = "Page supprimée"
|
||||
"Pages" = "Pages"
|
||||
"Pages added to the document" = "Pages ajoutées au document"
|
||||
"Pick the Frame tool, then drag on the canvas or choose a device size" = "Choisissez l’outil Cadre, puis faites glisser sur le canevas ou choisissez une taille d’appareil"
|
||||
|
||||
@@ -933,6 +933,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "Pengaturan cetak PDF"
|
||||
"Padding" = "Padding"
|
||||
"Page" = "Halaman"
|
||||
"Page deleted" = "Halaman dihapus"
|
||||
"Pages" = "Halaman"
|
||||
"Pages added to the document" = "Halaman ditambahkan ke dokumen"
|
||||
"Placed on the current page" = "Ditempatkan di halaman saat ini"
|
||||
|
||||
@@ -938,6 +938,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF 印刷設定"
|
||||
"Padding" = "パディング"
|
||||
"Page" = "ページ"
|
||||
"Page deleted" = "ページは削除済み"
|
||||
"Pages" = "ページ"
|
||||
"Pages added to the document" = "ページをドキュメントに追加した"
|
||||
"Placed on the current page" = "現在のページに配置した"
|
||||
|
||||
@@ -892,6 +892,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF 인쇄 설정"
|
||||
"Padding" = "패딩"
|
||||
"Page" = "페이지"
|
||||
"Page deleted" = "페이지가 삭제됨"
|
||||
"Pages" = "페이지"
|
||||
"Pages added to the document" = "문서에 페이지 추가됨"
|
||||
"Placed on the current page" = "현재 페이지에 배치됨"
|
||||
|
||||
@@ -952,6 +952,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF-afdrukinstellingen"
|
||||
"Padding" = "Padding"
|
||||
"Page" = "Pagina"
|
||||
"Page deleted" = "Pagina verwijderd"
|
||||
"Pages" = "Pagina's"
|
||||
"Pages added to the document" = "Pagina's toegevoegd aan het document"
|
||||
"Pages: {pages}" = "Pagina's: {pages}"
|
||||
|
||||
@@ -891,6 +891,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "Configurações de impressão em PDF"
|
||||
"Padding" = "Espaçamento interno"
|
||||
"Page" = "Página"
|
||||
"Page deleted" = "Página excluída"
|
||||
"Pages" = "Páginas"
|
||||
"Pages added to the document" = "Páginas adicionadas ao documento"
|
||||
"Placed on the current page" = "Colocado na página atual"
|
||||
|
||||
@@ -932,6 +932,7 @@ name = "Русский"
|
||||
"PDF print settings" = "Настройки печати PDF"
|
||||
"Padding" = "Внутренние отступы"
|
||||
"Page" = "Страница"
|
||||
"Page deleted" = "Страница удалена"
|
||||
"Pages" = "Страницы"
|
||||
"Pages added to the document" = "Страницы добавлены в документ"
|
||||
"Placed on the current page" = "Размещено на текущей странице"
|
||||
|
||||
@@ -942,6 +942,7 @@ name = "Svenska"
|
||||
"PDF print settings" = "PDF-utskriftsinställningar"
|
||||
"Padding" = "Utfyllnad"
|
||||
"Page" = "Sida"
|
||||
"Page deleted" = "Sidan har raderats"
|
||||
"Pages" = "Sidor"
|
||||
"Pages added to the document" = "Sidor tillagda i dokumentet"
|
||||
"Placed on the current page" = "Placerat på den aktuella sidan"
|
||||
|
||||
@@ -933,6 +933,7 @@ name = "Türkçe"
|
||||
"PDF print settings" = "PDF baskı ayarları"
|
||||
"Padding" = "İç boşluk"
|
||||
"Page" = "Sayfa"
|
||||
"Page deleted" = "Sayfa silindi"
|
||||
"Pages" = "Sayfalar"
|
||||
"Pages added to the document" = "Sayfalar belgeye eklendi"
|
||||
"Placed on the current page" = "Geçerli sayfaya yerleştirildi"
|
||||
|
||||
@@ -893,6 +893,7 @@ status = "draft — needs native review"
|
||||
"PDF print settings" = "PDF 打印设置"
|
||||
"Padding" = "内边距"
|
||||
"Page" = "页面"
|
||||
"Page deleted" = "页面已删除"
|
||||
"Pages" = "页面"
|
||||
"Pages added to the document" = "页面已添加到文档"
|
||||
"Placed on the current page" = "已放置在当前页面"
|
||||
|
||||
@@ -544,6 +544,7 @@ fn input(app: &mut FigApp, ui: &mut Ui, resp: &egui::Response) -> bool {
|
||||
// A press on the canvas gives the keyboard back to it (arrows nudge again).
|
||||
if resp.hovered() && ctx.input(|i| i.pointer.any_pressed()) {
|
||||
app.ui.layers_focus = false;
|
||||
app.ui.pages_focus = false;
|
||||
app.ui.key_box = None;
|
||||
}
|
||||
// Zoom (pinch, Cmd/Ctrl+scroll) about the pointer; plain scroll pans.
|
||||
|
||||
@@ -199,6 +199,7 @@ fn pages(app: &mut FigApp, ui: &mut Ui) {
|
||||
}
|
||||
icons::paint(ui.painter(), ar.shrink(5.0), Icon::Plus, t.icon);
|
||||
if aresp.clicked() {
|
||||
app.ui.pages_focus = true;
|
||||
app.run("page.add", json!({}));
|
||||
}
|
||||
if !app.ui.pages_open {
|
||||
@@ -257,6 +258,8 @@ fn pages(app: &mut FigApp, ui: &mut Ui) {
|
||||
}
|
||||
if resp.clicked() && !divider {
|
||||
select = Some(id);
|
||||
app.ui.pages_focus = true;
|
||||
app.ui.layers_focus = false;
|
||||
}
|
||||
if resp.double_clicked() {
|
||||
app.ui.rename_page = Some((id, name.clone()));
|
||||
@@ -274,7 +277,7 @@ fn pages(app: &mut FigApp, ui: &mut Ui) {
|
||||
ui.close();
|
||||
}
|
||||
if ui.button(tr("Delete page")).clicked() {
|
||||
app.run("page.delete", json!({"id": id}));
|
||||
delete_page(app, id);
|
||||
ui.close();
|
||||
}
|
||||
});
|
||||
@@ -311,6 +314,13 @@ fn pages(app: &mut FigApp, ui: &mut Ui) {
|
||||
pages_edge(app, ui, moved.inner_rect.height().min(moved.content_size.y));
|
||||
}
|
||||
|
||||
/// Delete `page` (no confirmation: Undo brings it back) and say so.
|
||||
pub(crate) fn delete_page(app: &mut FigApp, page: NodeId) {
|
||||
if app.run("page.delete", json!({"id": page})).is_some() {
|
||||
app.toast(tr("Page deleted"));
|
||||
}
|
||||
}
|
||||
|
||||
/// The pages list's height in a panel with `avail` below the Pages header: the height asked for
|
||||
/// (by default 40% of it), at least three rows, and leaving the layers their header and three rows.
|
||||
pub(crate) fn pages_list_height(asked: Option<f32>, avail: f32) -> f32 {
|
||||
@@ -1064,6 +1074,7 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
|
||||
}
|
||||
if let Some((id, m)) = clicked {
|
||||
app.ui.layers_focus = true;
|
||||
app.ui.pages_focus = false;
|
||||
let order: Vec<NodeId> = rows.iter().map(|r| r.id).collect();
|
||||
let pos = |x: NodeId| order.iter().position(|o| *o == x);
|
||||
let anchor = app.ui.layer_anchor.filter(|a| sel.contains(a)).and_then(pos);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Arrow keys in the layers panel once a row has been clicked: up/down through the rows (Shift
|
||||
//! extends), left/right to collapse, expand, and go to the parent or first child; a press on the
|
||||
//! canvas gives arrows back to nudging.
|
||||
//! canvas gives arrows back to nudging. Delete after a click in the Pages list deletes the page.
|
||||
|
||||
use egui::{Event, Key, Modifiers, pos2};
|
||||
use egui::{Event, Key, Modifiers, Pos2, pos2, vec2};
|
||||
use figcraft_doc::NodeId;
|
||||
use figcraft_engine::Session;
|
||||
use serde_json::json;
|
||||
@@ -107,3 +107,80 @@ fn a_canvas_press_gives_arrows_back_to_nudging() {
|
||||
let doc = u.app.session.doc().unwrap();
|
||||
assert_eq!(doc.node(c).unwrap().transform.translation().x, 201.0);
|
||||
}
|
||||
|
||||
/// The middle of `page`'s row in the Pages list, found by clicking down the list until it opens;
|
||||
/// then back to `back`.
|
||||
fn page_row(u: &mut Ui, page: NodeId, back: NodeId) -> Pos2 {
|
||||
for y in (90..400).step_by(4) {
|
||||
let p = pos2(150.0, y as f32);
|
||||
u.button(p, true);
|
||||
u.button(p, false);
|
||||
if u.app.session.page().unwrap() == page {
|
||||
u.app.session.execute("page.select", &json!({"id": back})).unwrap();
|
||||
u.frame(vec![]);
|
||||
// Rows are 32 high: aim at the middle, past the hit tolerance at its edge.
|
||||
return p + vec2(0.0, 12.0);
|
||||
}
|
||||
}
|
||||
panic!("no row for page {page:?}");
|
||||
}
|
||||
|
||||
fn pages(u: &Ui) -> usize {
|
||||
u.app.session.doc().unwrap().pages.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_after_clicking_a_page_deletes_it_without_asking() {
|
||||
// #859: Delete on a page picked in the Pages list said "nothing is selected".
|
||||
let (mut u, ..) = setup();
|
||||
let first = u.app.session.page().unwrap();
|
||||
let second = id(u.app.session.execute("page.add", &json!({})).unwrap());
|
||||
u.app.session.execute("page.select", &json!({"id": first})).unwrap();
|
||||
u.frame(vec![]);
|
||||
let row = page_row(&mut u, second, first);
|
||||
u.button(row, true);
|
||||
u.button(row, false);
|
||||
assert_eq!(u.app.session.page().unwrap(), second);
|
||||
key(&mut u, Key::Delete, false);
|
||||
// Gone at once, no confirmation: a toast says so and Undo brings it back.
|
||||
assert_eq!(pages(&u), 1);
|
||||
assert!(!u.app.session.doc().unwrap().pages.contains(&second));
|
||||
assert_eq!(u.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Page deleted"));
|
||||
u.app.run("edit.undo", json!({}));
|
||||
assert_eq!(pages(&u), 2);
|
||||
// Backspace (the Mac's delete key) does the same.
|
||||
let row = page_row(&mut u, second, first);
|
||||
u.button(row, true);
|
||||
u.button(row, false);
|
||||
key(&mut u, Key::Backspace, false);
|
||||
assert_eq!(pages(&u), 1);
|
||||
// The last page stays.
|
||||
key(&mut u, Key::Delete, false);
|
||||
assert_eq!(pages(&u), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_layers_or_a_canvas_press_take_delete_back_from_the_pages_list() {
|
||||
let (mut u, a, ..) = setup();
|
||||
let first = u.app.session.page().unwrap();
|
||||
let second = id(u.app.session.execute("page.add", &json!({})).unwrap());
|
||||
u.frame(vec![]);
|
||||
let row = page_row(&mut u, first, second);
|
||||
u.button(row, true);
|
||||
u.button(row, false);
|
||||
assert_eq!(u.app.session.page().unwrap(), first);
|
||||
assert!(u.app.ui.pages_focus);
|
||||
// Layers selected after the click (Select all, say): Delete deletes them, not the page.
|
||||
u.app.run("select.set", json!({"ids": [a]}));
|
||||
key(&mut u, Key::Delete, false);
|
||||
assert_eq!(pages(&u), 2);
|
||||
assert!(u.app.session.doc().unwrap().node(a).is_none());
|
||||
// A press on the canvas gives Delete back to it.
|
||||
let p = pos2(900.0, 700.0);
|
||||
u.button(p, true);
|
||||
u.button(p, false);
|
||||
assert!(!u.app.ui.pages_focus);
|
||||
u.app.run("select.none", json!({}));
|
||||
key(&mut u, Key::Delete, false);
|
||||
assert_eq!(pages(&u), 2, "Delete with nothing selected on the canvas deletes no page");
|
||||
}
|
||||
|
||||
@@ -500,6 +500,10 @@ pub struct UiState {
|
||||
/// The layers panel has the keyboard: arrow keys move through its rows instead of nudging
|
||||
/// (set by a click on a row, cleared by a press on the canvas).
|
||||
pub layers_focus: bool,
|
||||
/// The pages list has the keyboard: Delete with no layers selected deletes the current page (set
|
||||
/// by a click on a page row or the add-page button, cleared by a click on a layer row or a press
|
||||
/// on the canvas).
|
||||
pub pages_focus: bool,
|
||||
pub layers_seen_sel: Vec<NodeId>,
|
||||
pub dark: bool,
|
||||
pub follow_system: bool,
|
||||
@@ -634,6 +638,7 @@ impl Default for UiState {
|
||||
layer_filter: String::new(),
|
||||
layer_anchor: None,
|
||||
layers_focus: false,
|
||||
pages_focus: false,
|
||||
layers_seen_sel: Vec::new(),
|
||||
dark: false,
|
||||
follow_system: false,
|
||||
|
||||
@@ -117,6 +117,12 @@ pub fn invoke(app: &mut FigApp, id: &str) {
|
||||
// Edit ▸ Delete (and the Delete binding) with an interaction open in the Prototype tab
|
||||
// removes that interaction, not the layer (#767).
|
||||
"edit.delete" if app.ui_context.clone().is_some_and(|ctx| crate::proto_panel::delete_selected_interaction(app, &ctx)) => {}
|
||||
// After a click in the Pages list, with no layers selected, it deletes the current page (#859).
|
||||
"edit.delete" if app.ui.pages_focus && app.session.selection().is_empty() => {
|
||||
if let Ok(page) = app.session.page() {
|
||||
crate::layers::delete_page(app, page);
|
||||
}
|
||||
}
|
||||
"file.open" => app.open_dialog(),
|
||||
"plugin.import" => crate::plugins_ui::import_dialog(app),
|
||||
"file.import" => app.import_dialog(false),
|
||||
|
||||
@@ -217,6 +217,7 @@ pub fn input(app: &mut FigApp, ctx: &egui::Context, pos: Option<Pos2>, hovered:
|
||||
let drag =
|
||||
Drag { ids, axis: axis.clone(), start: p, value: h.value, uid: ds.uid, page: ds.page, moved: false, last_value: axis.value };
|
||||
app.ui.layers_focus = false;
|
||||
app.ui.pages_focus = false;
|
||||
app.ui.key_box = None;
|
||||
ctx.memory_mut(|m| {
|
||||
if let Some(f) = m.focused() {
|
||||
|
||||
Reference in New Issue
Block a user