mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3281 from gorkematlyn/upstream-ui8
UI.8: collapse dock groups to their tabs
This commit is contained in:
@@ -127,6 +127,7 @@ fn panel_icon(app: &mut VectorcraftApp, ui: &mut Ui, id: &'static str, label: &s
|
||||
}
|
||||
|
||||
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
crate::panel_docking::prune_collapsed(&mut app.ui);
|
||||
if crate::panel_docking::show(app, ui) {
|
||||
return;
|
||||
}
|
||||
@@ -159,6 +160,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
// It floats from this frame on.
|
||||
return;
|
||||
}
|
||||
// Collapsed to its tabs (a double-click on one).
|
||||
if app.ui.docking_collapsed.contains(app.ui.dock_tab.info().0) {
|
||||
return;
|
||||
}
|
||||
egui::Frame::NONE.inner_margin(egui::Margin { left: 12, right: 10, top: 10, bottom: 8 }).show(ui, |ui| match active {
|
||||
DockTab::Properties => {
|
||||
egui::ScrollArea::vertical()
|
||||
@@ -230,9 +235,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
});
|
||||
}
|
||||
|
||||
/// The tabbed group's tab strip, showing `active`: a click on a tab shows it, a tab dragged out of
|
||||
/// the strip floats on its own, and the strip right of the tabs floats the whole group. True when
|
||||
/// the shown panel floats now.
|
||||
/// The tabbed group's tab strip, showing `active`: a click on a tab shows it, a double-click
|
||||
/// collapses the group to its tabs or expands it, a tab dragged out of the strip floats on its own,
|
||||
/// and the strip right of the tabs floats the whole group. True when the shown panel floats now.
|
||||
fn tab_strip(app: &mut VectorcraftApp, ui: &mut Ui, active: DockTab) -> bool {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let (strip, _) = ui.allocate_exact_size(vec2(ui.available_width(), 33.0), Sense::hover());
|
||||
@@ -258,6 +263,10 @@ fn tab_strip(app: &mut VectorcraftApp, ui: &mut Ui, active: DockTab) -> bool {
|
||||
let id = tab.info().0;
|
||||
if let Some((from, to)) = tab_reorder_on_release(strip, pointer, resp, &out.responses) {
|
||||
apply_dock_tab_reorder(app, &tabs, from, to);
|
||||
} else if resp.double_clicked() {
|
||||
if let Err(error) = app.run("window.panel.collapse", json!({ "panel": id })) {
|
||||
app.ui.status = error;
|
||||
}
|
||||
} else if resp.clicked() {
|
||||
app.ui.dock_tab = tab;
|
||||
}
|
||||
|
||||
@@ -583,6 +583,7 @@ pub fn track(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
app.ui.docking = m.original.docking;
|
||||
app.ui.docking_hidden = m.original.docking_hidden;
|
||||
app.ui.docking_icons = m.original.docking_icons;
|
||||
app.ui.docking_collapsed = m.original.docking_collapsed;
|
||||
app.ui.floating_panels = m.original.floating_panels;
|
||||
app.ui.open_panel = m.original.open_panel;
|
||||
app.ui.dock_tab = m.original.dock_tab;
|
||||
|
||||
@@ -4474,6 +4474,7 @@ effect Soft Меко
|
||||
Activate Panel Активиране на панела
|
||||
Change Panel Layout Промяна на оформлението на панела
|
||||
Close Panel Затваряне на панела
|
||||
Collapse Panel Group Свиване на групата панели
|
||||
Move Panel Преместване на панела
|
||||
Group with Групиране с
|
||||
Resize panels Преоразмеряване на панели
|
||||
@@ -4484,6 +4485,9 @@ effect Soft Меко
|
||||
@msg before must name a panel параметърът „преди“ трябва да указва панел
|
||||
@msg docking action contains an unknown panel or an excessive split path действието по закачане съдържа непознат панел или прекомерен път на разделяне
|
||||
@msg group must be true or false групата трябва да е истина или лъжа
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} панелът е плаващ; свийте групата му с window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group панелът не е в група на дока
|
||||
@msg only a dock group above or below others collapses, not one beside another свива се само група на дока над или под други, не и такава до друга
|
||||
@msg icon must be true or false icon трябва да е истина или лъжа
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon закачва панел само в колоната с икони, без anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column закачва набор като колона на дока, без anchor или icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -829,6 +829,7 @@
|
||||
Activate Panel Aktivovat panel
|
||||
Change Panel Layout Změnit rozložení panelů
|
||||
Close Panel Zavřít panel
|
||||
Collapse Panel Group Sbalit skupinu panelů
|
||||
Move Panel Přesunout panel
|
||||
Group with Seskupit s
|
||||
Resize panels Změnit velikost panelů
|
||||
@@ -4752,6 +4753,9 @@ imageTrace Outline Obrys
|
||||
@msg before must name a panel before musí určovat panel
|
||||
@msg docking action contains an unknown panel or an excessive split path akce ukotvení obsahuje neznámý panel nebo příliš dlouhou cestu rozdělení
|
||||
@msg group must be true or false group musí být true nebo false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} panel je plovoucí; jeho skupinu sbalíte pomocí window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group panel není ve skupině doku
|
||||
@msg only a dock group above or below others collapses, not one beside another sbalit lze jen skupinu doku nad nebo pod jinými, ne vedle jiné
|
||||
@msg icon must be true or false icon musí být true nebo false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon jen ukotví panel ve sloupci ikon, bez anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column ukotví sadu jako sloupec doku, bez anchor nebo icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -761,6 +761,7 @@
|
||||
Activate Panel Aktivér panel
|
||||
Change Panel Layout Skift panelets layout
|
||||
Close Panel Luk panel
|
||||
Collapse Panel Group Skjul panelgruppe
|
||||
Move Panel Flyt panel
|
||||
Group with Gruppér med
|
||||
Resize panels Tilpas størrelse på paneler
|
||||
@@ -4450,6 +4451,9 @@ imageTrace Outline Kontur
|
||||
@msg before must name a panel before skal angive et panel
|
||||
@msg docking action contains an unknown panel or an excessive split path dokningshandlingen indeholder et ukendt panel eller en overdreven opdelingssti
|
||||
@msg group must be true or false group skal være true eller false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} panelet flyder; skjul dets gruppe med window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group panelet er ikke i en dockgruppe
|
||||
@msg only a dock group above or below others collapses, not one beside another kun en dockgruppe over eller under andre kan skjules, ikke en ved siden af en anden
|
||||
@msg icon must be true or false icon skal være true eller false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon docker kun et panel i ikonkolonnen, uden anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column docker et sæt som en dockkolonne, uden anchor eller icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4554,6 +4554,7 @@ theme Appearance Erscheinungsbild
|
||||
Activate Panel Bedienfeld aktivieren
|
||||
Change Panel Layout Bedienfeldanordnung ändern
|
||||
Close Panel Bedienfeld schließen
|
||||
Collapse Panel Group Bedienfeldgruppe minimieren
|
||||
Move Panel Bedienfeld verschieben
|
||||
Group with Gruppieren mit
|
||||
Resize panel window Bedienfeldfenster skalieren
|
||||
@@ -4564,6 +4565,9 @@ theme Appearance Erscheinungsbild
|
||||
@msg before must name a panel before muss ein Bedienfeld benennen
|
||||
@msg docking action contains an unknown panel or an excessive split path Die Docking-Aktion enthält ein unbekanntes Bedienfeld oder einen zu langen Teilungspfad
|
||||
@msg group must be true or false group muss true oder false sein
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} Bedienfeld ist abgelöst; seine Gruppe mit window.panel.float {panel, collapsed} minimieren
|
||||
@msg panel is not in a dock group Bedienfeld ist in keiner Dock-Gruppe
|
||||
@msg only a dock group above or below others collapses, not one beside another nur eine Dock-Gruppe über oder unter anderen lässt sich minimieren, nicht eine daneben
|
||||
@msg icon must be true or false icon muss true oder false sein
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon dockt ein Bedienfeld nur in der Symbolspalte an, ohne anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column dockt einen Satz als Dock-Spalte an, ohne anchor oder icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2229,6 +2229,7 @@ effect Soft Απαλό
|
||||
Activate Panel Ενεργοποίηση πάνελ
|
||||
Change Panel Layout Αλλαγή διάταξης πάνελ
|
||||
Close Panel Κλείσιμο πάνελ
|
||||
Collapse Panel Group Σύμπτυξη ομάδας πάνελ
|
||||
Move Panel Μετακίνηση πάνελ
|
||||
Group with Ομαδοποίηση με
|
||||
Resize panels Αλλαγή μεγέθους πάνελ
|
||||
@@ -4469,6 +4470,9 @@ theme Appearance Εμφάνιση
|
||||
@msg before must name a panel το before πρέπει να ορίζει πίνακα
|
||||
@msg docking action contains an unknown panel or an excessive split path η ενέργεια σύνδεσης περιέχει άγνωστο πίνακα ή υπερβολικά βαθιά διαδρομή διαίρεσης
|
||||
@msg group must be true or false το group πρέπει να είναι true ή false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} Το πάνελ αιωρείται· συμπτύξτε την ομάδα του με window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group Το πάνελ δεν βρίσκεται σε ομάδα αγκύρωσης
|
||||
@msg only a dock group above or below others collapses, not one beside another συμπτύσσεται μόνο ομάδα αγκύρωσης πάνω ή κάτω από άλλες, όχι δίπλα σε άλλη
|
||||
@msg icon must be true or false το icon πρέπει να είναι true ή false
|
||||
@msg icon only docks a panel in the icon column, with no anchor το icon αγκυρώνει πάνελ μόνο στη στήλη εικονιδίων, χωρίς anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon το column αγκυρώνει ένα σύνολο ως στήλη αγκύρωσης, χωρίς anchor ή icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2039 and column 19.
|
@@ -4557,6 +4557,7 @@ axis Both Ambos
|
||||
Activate Panel Activar panel
|
||||
Change Panel Layout Cambiar distribución de paneles
|
||||
Close Panel Cerrar panel
|
||||
Collapse Panel Group Contraer grupo de paneles
|
||||
Move Panel Mover panel
|
||||
Group with Agrupar con
|
||||
Resize panels Cambiar tamaño de los paneles
|
||||
@@ -4567,6 +4568,9 @@ axis Both Ambos
|
||||
@msg before must name a panel before debe indicar un panel
|
||||
@msg docking action contains an unknown panel or an excessive split path La acción de anclaje contiene un panel desconocido o una ruta de divisiones demasiado larga
|
||||
@msg group must be true or false group debe ser true o false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} el panel flota; contraiga su grupo con window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group el panel no está en un grupo del acoplamiento
|
||||
@msg only a dock group above or below others collapses, not one beside another solo se contrae un grupo del acoplamiento situado encima o debajo de otros, no al lado
|
||||
@msg icon must be true or false icon debe ser true o false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon solo acopla un panel en la columna de iconos, sin anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column acopla un conjunto como columna del acoplamiento, sin anchor ni icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -812,6 +812,7 @@
|
||||
Activate Panel Aktivoi paneeli
|
||||
Change Panel Layout Muuta paneelien asettelua
|
||||
Close Panel Sulje paneeli
|
||||
Collapse Panel Group Kutista paneeliryhmä
|
||||
Move Panel Siirrä paneelia
|
||||
Group with Ryhmitä kohteen kanssa
|
||||
Resize panels Muuta paneelien kokoa
|
||||
@@ -4661,6 +4662,9 @@ imageTrace Outline Ääriviiva
|
||||
@msg before must name a panel before-arvon on nimettävä paneeli
|
||||
@msg docking action contains an unknown panel or an excessive split path telakointitoiminto sisältää tuntemattoman paneelin tai liian pitkän jakopolun
|
||||
@msg group must be true or false group-arvon on oltava true tai false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} paneeli kelluu; kutista sen ryhmä komennolla window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group paneeli ei ole telakkaryhmässä
|
||||
@msg only a dock group above or below others collapses, not one beside another vain muiden ylä- tai alapuolella oleva telakkaryhmä kutistuu, ei toisen vieressä oleva
|
||||
@msg icon must be true or false icon-arvon on oltava true tai false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon telakoi paneelin vain kuvakesarakkeeseen, ilman anchor-arvoa
|
||||
@msg column docks a set as a dock column, with no anchor or icon column telakoi joukon telakkasarakkeeksi, ilman anchor- tai icon-arvoa
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4569,6 +4569,7 @@ effect Soft Doux
|
||||
Activate Panel Activer le panneau
|
||||
Change Panel Layout Modifier la disposition des panneaux
|
||||
Close Panel Fermer le panneau
|
||||
Collapse Panel Group Réduire le groupe de panneaux
|
||||
Move Panel Déplacer le panneau
|
||||
Group with Regrouper avec
|
||||
Resize panels Redimensionner les panneaux
|
||||
@@ -4579,6 +4580,9 @@ effect Soft Doux
|
||||
@msg before must name a panel before doit désigner un panneau
|
||||
@msg docking action contains an unknown panel or an excessive split path L’action d’ancrage contient un panneau inconnu ou un chemin de divisions trop long
|
||||
@msg group must be true or false group doit être true ou false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} le panneau est flottant ; réduisez son groupe avec window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group le panneau n’est dans aucun groupe du dock
|
||||
@msg only a dock group above or below others collapses, not one beside another seul un groupe du dock situé au-dessus ou au-dessous d’autres se réduit, pas un groupe à côté
|
||||
@msg icon must be true or false icon doit être true ou false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon ne fait qu’ancrer un panneau dans la colonne d’icônes, sans anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column ancre un ensemble comme colonne du dock, sans anchor ni icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -641,6 +641,7 @@
|
||||
Close All Összes bezárása
|
||||
Close Library Könyvtár bezárása
|
||||
Close Panel Panel bezárása
|
||||
Collapse Panel Group Panelcsoport összecsukása
|
||||
Close Path Útvonal lezárása
|
||||
Close paths when ends are within: Útvonalak lezárása, ha a végek távolsága legfeljebb:
|
||||
Clumped Csomós
|
||||
@@ -4595,6 +4596,9 @@ imageTrace Outline Körvonal
|
||||
@msg give the path `id` and the width point `index` add meg az útvonal `id` és a szélességi pont `index` értékét
|
||||
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it a színátmenet nincs megadva a fájlban: „{name}”; helyette egyszínű kitöltés áll
|
||||
@msg group must be true or false a group értéke true vagy false lehet
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} a panel lebeg; a csoportját a window.panel.float {panel, collapsed} paranccsal csukhatja össze
|
||||
@msg panel is not in a dock group a panel nincs dokkolt csoportban
|
||||
@msg only a dock group above or below others collapses, not one beside another csak a többi fölötti vagy alatti dokkolt csoport csukható össze, a mellettük lévő nem
|
||||
@msg icon must be true or false az icon értéke true vagy false lehet
|
||||
@msg icon only docks a panel in the icon column, with no anchor az icon csak az ikonoszlopba dokkol egy panelt, anchor nélkül
|
||||
@msg column docks a set as a dock column, with no anchor or icon a column egy készletet dokkoszlopként dokkol, anchor és icon nélkül
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -761,6 +761,7 @@
|
||||
Activate Panel Aktifkan panel
|
||||
Change Panel Layout Ubah tata letak panel
|
||||
Close Panel Tutup panel
|
||||
Collapse Panel Group Ciutkan grup panel
|
||||
Move Panel Pindahkan panel
|
||||
Group with Grupkan dengan
|
||||
Resize panels Ubah ukuran panel
|
||||
@@ -4450,6 +4451,9 @@ imageTrace Outline Kontur
|
||||
@msg before must name a panel before harus menyebutkan panel
|
||||
@msg docking action contains an unknown panel or an excessive split path tindakan docking berisi panel tidak dikenal atau jalur pemisahan berlebihan
|
||||
@msg group must be true or false group harus true atau false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} panel mengambang; ciutkan grupnya dengan window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group panel tidak berada dalam grup dok
|
||||
@msg only a dock group above or below others collapses, not one beside another hanya grup dok di atas atau di bawah grup lain yang bisa diciutkan, bukan yang berada di sampingnya
|
||||
@msg icon must be true or false icon harus true atau false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon hanya menambatkan panel di kolom ikon, tanpa anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column menambatkan satu set sebagai kolom dok, tanpa anchor atau icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3810 and column 19.
|
@@ -4599,6 +4599,7 @@ effect Regular Normale
|
||||
Activate Panel Attiva pannello
|
||||
Change Panel Layout Modifica disposizione dei pannelli
|
||||
Close Panel Chiudi pannello
|
||||
Collapse Panel Group Comprimi gruppo di pannelli
|
||||
Move Panel Sposta pannello
|
||||
Group with Raggruppa con
|
||||
Resize panels Ridimensiona pannelli
|
||||
@@ -4609,6 +4610,9 @@ effect Regular Normale
|
||||
@msg before must name a panel before deve indicare un pannello
|
||||
@msg docking action contains an unknown panel or an excessive split path L’azione di ancoraggio contiene un pannello sconosciuto o un percorso di divisioni troppo lungo
|
||||
@msg group must be true or false group deve essere true o false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} il pannello è mobile; comprimi il suo gruppo con window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group il pannello non è in un gruppo del dock
|
||||
@msg only a dock group above or below others collapses, not one beside another si comprime solo un gruppo del dock sopra o sotto altri, non uno accanto
|
||||
@msg icon must be true or false icon deve essere true o false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon ancora un pannello solo nella colonna delle icone, senza anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column ancora un insieme come colonna del dock, senza anchor né icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4638,6 +4638,7 @@ effect Vertical 垂直
|
||||
Activate Panel パネルをアクティブにする
|
||||
Change Panel Layout パネルの配置を変更
|
||||
Close Panel パネルを閉じる
|
||||
Collapse Panel Group パネルグループを折りたたむ
|
||||
Move Panel パネルを移動
|
||||
Group with グループ化先
|
||||
Resize panels パネルのサイズを変更
|
||||
@@ -4649,6 +4650,9 @@ effect Vertical 垂直
|
||||
@msg before must name a panel before にパネルを指定してください
|
||||
@msg docking action contains an unknown panel or an excessive split path ドッキング操作に不明なパネルまたは長すぎる分割パスが含まれています
|
||||
@msg group must be true or false group は true または false にしてください
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} パネルはフローティング中です。グループは window.panel.float {panel, collapsed} で折りたたんでください
|
||||
@msg panel is not in a dock group パネルはドックのグループにありません
|
||||
@msg only a dock group above or below others collapses, not one beside another 折りたためるのは他のグループの上下にあるドックのグループだけで、横に並んだグループは折りたためません
|
||||
@msg icon must be true or false icon は true または false である必要があります
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon はパネルをアイコン列にのみドッキングします(anchor なし)
|
||||
@msg column docks a set as a dock column, with no anchor or icon column はセットをドック列としてドッキングします(anchor・icon なし)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -703,6 +703,7 @@
|
||||
Activate Panel პანელის გააქტიურება
|
||||
Change Panel Layout პანელების განლაგების შეცვლა
|
||||
Close Panel პანელის დახურვა
|
||||
Collapse Panel Group პანელების ჯგუფის აკეცვა
|
||||
Move Panel პანელის გადაადგილება
|
||||
Group with ერთად დაჯგუფება
|
||||
Resize panels პანელების ზომის შეცვლა
|
||||
@@ -4201,6 +4202,9 @@ effect Regular ჩვეულებრივი
|
||||
@msg before must name a panel before უნდა ასახელებდეს პანელს
|
||||
@msg docking action contains an unknown panel or an excessive split path მიმაგრების მოქმედება შეიცავს უცნობ პანელს ან ზედმეტად გრძელ გაყოფის გზას
|
||||
@msg group must be true or false group უნდა იყოს true ან false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} პანელი მცურავია; მისი ჯგუფი აკეცეთ window.panel.float {panel, collapsed}-ით
|
||||
@msg panel is not in a dock group პანელი არ არის დოკის ჯგუფში
|
||||
@msg only a dock group above or below others collapses, not one beside another იკეცება მხოლოდ სხვების ზემოთ ან ქვემოთ მდებარე დოკის ჯგუფი, არა სხვის გვერდით მდებარე
|
||||
@msg icon must be true or false icon უნდა იყოს true ან false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon პანელს მხოლოდ ხატულების სვეტში ამაგრებს, anchor-ის გარეშე
|
||||
@msg column docks a set as a dock column, with no anchor or icon column ნაკრებს დოკის სვეტად ამაგრებს, anchor-ის ან icon-ის გარეშე
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4568,6 +4568,7 @@ effect Vertical 세로
|
||||
Activate Panel 패널 활성화
|
||||
Change Panel Layout 패널 레이아웃 변경
|
||||
Close Panel 패널 닫기
|
||||
Collapse Panel Group 패널 그룹 접기
|
||||
Move Panel 패널 이동
|
||||
Group with 함께 그룹화
|
||||
Resize panels 패널 크기 조절
|
||||
@@ -4579,6 +4580,9 @@ effect Vertical 세로
|
||||
@msg before must name a panel before는 패널을 지정해야 합니다
|
||||
@msg docking action contains an unknown panel or an excessive split path 도킹 동작에 알 수 없는 패널이나 지나치게 많은 분할 경로가 있습니다
|
||||
@msg group must be true or false group은 true 또는 false여야 합니다
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} 패널이 분리되어 있습니다. 그룹은 window.panel.float {panel, collapsed}로 접으세요
|
||||
@msg panel is not in a dock group 패널이 도크 그룹에 없습니다
|
||||
@msg only a dock group above or below others collapses, not one beside another 다른 그룹의 위나 아래에 있는 도크 그룹만 접을 수 있으며, 옆에 있는 그룹은 접을 수 없습니다
|
||||
@msg icon must be true or false icon은 true 또는 false여야 합니다
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon은 anchor 없이 패널을 아이콘 열에만 도킹합니다
|
||||
@msg column docks a set as a dock column, with no anchor or icon column은 anchor나 icon 없이 세트를 도크 열로 도킹합니다
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -775,6 +775,7 @@
|
||||
Activate Panel Paneel activeren
|
||||
Change Panel Layout Paneelindeling wijzigen
|
||||
Close Panel Paneel sluiten
|
||||
Collapse Panel Group Paneelgroep samenvouwen
|
||||
Move Panel Paneel verplaatsen
|
||||
Group with Groeperen met
|
||||
Resize panels Panelen van formaat wijzigen
|
||||
@@ -4462,6 +4463,9 @@ imageTrace Outline Contour
|
||||
@msg before must name a panel before moet een paneel benoemen
|
||||
@msg docking action contains an unknown panel or an excessive split path dokactie bevat een onbekend paneel of een overmatig splitspad
|
||||
@msg group must be true or false group moet true of false zijn
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} paneel zweeft; vouw de groep samen met window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group paneel staat niet in een dockgroep
|
||||
@msg only a dock group above or below others collapses, not one beside another alleen een dockgroep boven of onder andere vouwt samen, niet een ernaast
|
||||
@msg icon must be true or false icon moet true of false zijn
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon dockt een paneel alleen in de pictogramkolom, zonder anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column dockt een set als dockkolom, zonder anchor of icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -770,6 +770,7 @@
|
||||
Activate Panel Aktywuj panel
|
||||
Change Panel Layout Zmień układ paneli
|
||||
Close Panel Zamknij panel
|
||||
Collapse Panel Group Zwiń grupę paneli
|
||||
Move Panel Przesuń panel
|
||||
Group with Grupuj z
|
||||
Resize panels Zmień rozmiar paneli
|
||||
@@ -4580,6 +4581,9 @@ imageTrace Outline Kontur
|
||||
@msg icon must be true or false icon musi mieć wartość true lub false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon tylko dokuje panel w kolumnie ikon, bez anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column dokuje zestaw jako kolumnę doku, bez anchor ani icon
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} panel jest pływający; zwiń jego grupę za pomocą window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group panel nie należy do żadnej grupy doku
|
||||
@msg only a dock group above or below others collapses, not one beside another zwinąć można tylko grupę doku nad lub pod innymi, a nie obok innej
|
||||
@msg move requires a destination panel przeniesienie wymaga panelu docelowego
|
||||
@msg move requires an existing destination panel przeniesienie wymaga istniejącego panelu docelowego
|
||||
@msg onto must name a floating panel onto musi wskazywać panel pływający
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3056,6 +3056,7 @@ imageTrace Outline Contorno
|
||||
Activate Panel Ativar Painel
|
||||
Change Panel Layout Alterar Layout dos Painéis
|
||||
Close Panel Fechar Painel
|
||||
Collapse Panel Group Recolher Grupo de Painéis
|
||||
Move Panel Mover Painel
|
||||
Group with Agrupar com
|
||||
Resize panels Redimensionar painéis
|
||||
@@ -4021,6 +4022,9 @@ axis Both Ambos
|
||||
@msg before must name a panel before deve nomear um painel
|
||||
@msg docking action contains an unknown panel or an excessive split path a ação de encaixe contém um painel desconhecido ou um caminho de divisão excessivo
|
||||
@msg group must be true or false group deve ser true ou false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} o painel está flutuando; recolha o grupo com window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group o painel não está em um grupo do encaixe
|
||||
@msg only a dock group above or below others collapses, not one beside another só um grupo do encaixe acima ou abaixo de outros é recolhido, não um ao lado
|
||||
@msg icon must be true or false icon deve ser true ou false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon apenas encaixa um painel na coluna de ícones, sem anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column encaixa um conjunto como coluna do encaixe, sem anchor nem icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4595,6 +4595,7 @@ axis Both Оба
|
||||
Activate Panel Активировать панель
|
||||
Change Panel Layout Изменить расположение панелей
|
||||
Close Panel Закрыть панель
|
||||
Collapse Panel Group Свернуть группу панелей
|
||||
Move Panel Переместить панель
|
||||
Group with Сгруппировать с
|
||||
Resize panels Изменить размер панелей
|
||||
@@ -4605,6 +4606,9 @@ axis Both Оба
|
||||
@msg before must name a panel before должен указывать панель
|
||||
@msg docking action contains an unknown panel or an excessive split path Действие прикрепления содержит неизвестную панель или слишком длинный путь разделений
|
||||
@msg group must be true or false group должен иметь значение true или false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} панель плавающая; сверните её группу через window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group панель не находится в группе дока
|
||||
@msg only a dock group above or below others collapses, not one beside another свернуть можно только группу дока над или под другими, а не рядом с ними
|
||||
@msg icon must be true or false icon должен быть true или false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon только закрепляет панель в столбце значков, без anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column закрепляет набор как столбец дока, без anchor и icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4480,6 +4480,7 @@ effect Soft Mjuk
|
||||
Activate Panel Aktivera panel
|
||||
Change Panel Layout Ändra panellayout
|
||||
Close Panel Stäng panel
|
||||
Collapse Panel Group Komprimera panelgrupp
|
||||
Move Panel Flytta panel
|
||||
Group with Gruppera med
|
||||
Resize panels Ändra storlek på paneler
|
||||
@@ -4490,6 +4491,9 @@ effect Soft Mjuk
|
||||
@msg before must name a panel before måste ange en panel
|
||||
@msg docking action contains an unknown panel or an excessive split path dockningsåtgärden innehåller en okänd panel eller en alltför djup delningssökväg
|
||||
@msg group must be true or false group måste vara true eller false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} panelen flyter; komprimera dess grupp med window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group panelen ligger inte i en dockningsgrupp
|
||||
@msg only a dock group above or below others collapses, not one beside another bara en dockningsgrupp ovanför eller under andra komprimeras, inte en bredvid en annan
|
||||
@msg icon must be true or false icon måste vara true eller false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon dockar bara en panel i ikonkolumnen, utan anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column dockar en uppsättning som en dockningskolumn, utan anchor eller icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -4581,6 +4581,7 @@ axis Both Обидві
|
||||
Activate Panel Активувати панель
|
||||
Change Panel Layout Змінити розташування панелей
|
||||
Close Panel Закрити панель
|
||||
Collapse Panel Group Згорнути групу панелей
|
||||
Move Panel Перемістити панель
|
||||
Group with Згрупувати з
|
||||
Resize panels Змінити розмір панелей
|
||||
@@ -4591,6 +4592,9 @@ axis Both Обидві
|
||||
@msg before must name a panel before має вказувати панель
|
||||
@msg docking action contains an unknown panel or an excessive split path Дія прикріплення містить невідому панель або надто довгий шлях поділів
|
||||
@msg group must be true or false group має мати значення true або false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} панель від’єднана; згорніть її групу через window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group панель не входить до групи дока
|
||||
@msg only a dock group above or below others collapses, not one beside another згорнути можна лише групу дока над або під іншими, а не поруч з ними
|
||||
@msg icon must be true or false icon має бути true або false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon лише закріплює панель у стовпці значків, без anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column закріплює набір як стовпець дока, без anchor та icon
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 733 and column 40.
|
@@ -554,6 +554,7 @@
|
||||
Close All Đóng tất cả
|
||||
Close Library Đóng thư viện
|
||||
Close Panel Đóng bảng
|
||||
Collapse Panel Group Thu gọn nhóm bảng
|
||||
Close Path Đóng đường dẫn
|
||||
Close paths when ends are within: Đóng đường dẫn khi hai đầu cách nhau trong:
|
||||
Clumped Dạng cụm
|
||||
@@ -4244,6 +4245,9 @@ theme Appearance Giao diện
|
||||
@msg icon must be true or false icon phải là true hoặc false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon chỉ neo bảng vào cột biểu tượng, không có anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column neo một bộ thành cột neo, không có anchor hay icon
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} bảng đang nổi; hãy thu gọn nhóm của nó bằng window.panel.float {panel, collapsed}
|
||||
@msg panel is not in a dock group bảng không thuộc nhóm nào của dock
|
||||
@msg only a dock group above or below others collapses, not one beside another chỉ thu gọn được nhóm dock nằm trên hoặc dưới các nhóm khác, không phải nhóm nằm cạnh
|
||||
@msg guides are locked đường căn bị khóa
|
||||
@msg handle must be in or out handle phải là in hoặc out
|
||||
@msg image '{key}' has no pixels and was left out ảnh '{key}' không có pixel và bị bỏ qua
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4022 and column 10.
|
@@ -4274,6 +4274,9 @@ imageTrace Outline 轮廓
|
||||
@msg before must name a panel before 必须指定面板
|
||||
@msg docking action contains an unknown panel or an excessive split path 停靠操作包含未知面板或过长的拆分路径
|
||||
@msg group must be true or false group 必须是 true 或 false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} 面板处于浮动状态;请使用 window.panel.float {panel, collapsed} 折叠其面板组
|
||||
@msg panel is not in a dock group 面板不在停放组中
|
||||
@msg only a dock group above or below others collapses, not one beside another 只有位于其他组上方或下方的停放组可以折叠,并排的不行
|
||||
@msg icon must be true or false icon 必须为 true 或 false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon 只会把面板停放到图标栏中,不能带 anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column 将一组面板停放为停放栏,不能带 anchor 或 icon
|
||||
@@ -4886,6 +4889,7 @@ effect Vertical 垂直
|
||||
Activate Panel 激活面板
|
||||
Change Panel Layout 更改面板布局
|
||||
Close Panel 关闭面板
|
||||
Collapse Panel Group 折叠面板组
|
||||
Move Panel 移动面板
|
||||
Group with 组合到
|
||||
Resize panels 调整面板大小
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2258,6 +2258,9 @@ file-filter Templates 範本
|
||||
@msg before must name a panel before 必須指定面板
|
||||
@msg docking action contains an unknown panel or an excessive split path 停駐動作包含未知的面板或過長的分割路徑
|
||||
@msg group must be true or false group 必須是 true 或 false
|
||||
@msg panel floats; collapse its group with window.panel.float {panel, collapsed} 面板處於浮動狀態;請使用 window.panel.float {panel, collapsed} 摺疊其面板群組
|
||||
@msg panel is not in a dock group 面板不在停駐群組中
|
||||
@msg only a dock group above or below others collapses, not one beside another 只有位於其他群組上方或下方的停駐群組可以摺疊,並排的不行
|
||||
@msg icon must be true or false icon 必須為 true 或 false
|
||||
@msg icon only docks a panel in the icon column, with no anchor icon 只會將面板停駐到圖示欄中,不能帶 anchor
|
||||
@msg column docks a set as a dock column, with no anchor or icon column 會將一組面板停駐為停駐欄,不能帶 anchor 或 icon
|
||||
@@ -4692,6 +4695,7 @@ effect Soft 柔和
|
||||
Activate Panel 啟用面板
|
||||
Change Panel Layout 變更面板配置
|
||||
Close Panel 關閉面板
|
||||
Collapse Panel Group 摺疊面板群組
|
||||
Move Panel 移動面板
|
||||
Group with 群組至
|
||||
Resize panels 調整面板大小
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -459,6 +459,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
),
|
||||
("window.panel.activate", "Activate Panel", "", "{panel} show and select a panel in its current group"),
|
||||
("window.panel.close", "Close Panel", "", "{panel} hide a panel"),
|
||||
(
|
||||
"window.panel.collapse",
|
||||
"Collapse Panel Group",
|
||||
"",
|
||||
"{panel, collapsed?: bool} show the dock group holding a panel as its tabs only (or its panel again), as a double-click on its tab does; the panel becomes the group's shown one. Without `collapsed` it toggles. Floating groups collapse through window.panel.float {panel, collapsed}. Returns {panel, collapsed}",
|
||||
),
|
||||
(
|
||||
"window.panel.float",
|
||||
"Float Panel",
|
||||
|
||||
@@ -12,6 +12,7 @@ struct LegacyDrag {
|
||||
open_panel: Option<String>,
|
||||
floating: Vec<crate::state::FloatingPanels>,
|
||||
icons: std::collections::BTreeSet<String>,
|
||||
collapsed: std::collections::BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -19,6 +20,7 @@ struct RootGroupDrag {
|
||||
panel: String,
|
||||
layout: Layout<String>,
|
||||
hidden: std::collections::BTreeMap<String, craft_ui::docking::Location<String>>,
|
||||
collapsed: std::collections::BTreeSet<String>,
|
||||
open_panel: Option<String>,
|
||||
generation: u64,
|
||||
}
|
||||
@@ -31,6 +33,7 @@ fn cancel_legacy_drag(app: &mut VectorcraftApp, ctx: &egui::Context) -> bool {
|
||||
app.ui.floating_panels = previous.floating;
|
||||
app.ui.docking = previous.layout;
|
||||
app.ui.docking_icons = previous.icons;
|
||||
app.ui.docking_collapsed = previous.collapsed;
|
||||
craft_ui::docking::cancel_drag::<String>(ctx, egui::Id::new("vectorcraft-panel-docking"));
|
||||
true
|
||||
} else {
|
||||
@@ -233,6 +236,9 @@ fn placement(params: &Value) -> Result<Placement<String>, String> {
|
||||
}
|
||||
|
||||
pub(crate) fn command(app: &mut VectorcraftApp, id: &str, params: &Value) -> Option<Result<Value, String>> {
|
||||
if id == "window.panel.collapse" {
|
||||
return Some(collapse(app, params));
|
||||
}
|
||||
// Docking into the dock's tree or its icon column is the shared layout's, whatever set the
|
||||
// panel floats in. A dock column is the native sets', so it doesn't combine with either.
|
||||
let to_icons = params.get("icon").is_some_and(|icon| icon != &Value::Bool(false));
|
||||
@@ -348,6 +354,16 @@ fn apply_action(app: &mut VectorcraftApp, action: Action<String>) -> Result<Valu
|
||||
Action::Activate { panel } => Some(panel.clone()),
|
||||
_ => None,
|
||||
};
|
||||
// A panel moved, floated or closed leaves its group: that group isn't collapsed any more, and
|
||||
// the panel doesn't take the collapse along.
|
||||
let moved = match &action {
|
||||
Action::Move { panel, .. }
|
||||
| Action::OpenAt { panel, .. }
|
||||
| Action::Open { panel, .. }
|
||||
| Action::Float { panel, .. }
|
||||
| Action::Close { panel } => Some(panel.clone()),
|
||||
_ => None,
|
||||
};
|
||||
layout.apply(action).map_err(|error| error.to_string())?;
|
||||
restore_float_fallback(&mut layout, fallback)?;
|
||||
if let Some((panel, location)) = returning {
|
||||
@@ -358,6 +374,9 @@ fn apply_action(app: &mut VectorcraftApp, action: Action<String>) -> Result<Valu
|
||||
}
|
||||
app.ui.docking_icons = icons;
|
||||
app.ui.docking = Some(layout);
|
||||
if let Some(panel) = moved {
|
||||
app.ui.docking_collapsed.remove(&panel);
|
||||
}
|
||||
if let Some(panel) = activated {
|
||||
for group in &mut app.ui.floating_panels {
|
||||
if group.panels.contains(&panel) {
|
||||
@@ -370,6 +389,130 @@ fn apply_action(app: &mut VectorcraftApp, action: Action<String>) -> Result<Valu
|
||||
Ok(json!({"layout": app.ui.docking}))
|
||||
}
|
||||
|
||||
/// The panels shown by the dock's groups that can collapse to their tabs: each group of the shared
|
||||
/// layout's tree that is the whole tree or above or below another (one beside another can't be
|
||||
/// shorter than it), or the tabbed group of the legacy dock.
|
||||
fn dock_groups_shown(ui: &crate::state::UiState) -> Vec<String> {
|
||||
let Some(layout) = &ui.docking else {
|
||||
return crate::floating::shown_tab(ui).map(|tab| tab.info().0.to_string()).into_iter().collect();
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
// Each node, and whether it may be shortened (the root, or in a top/bottom split).
|
||||
let mut pending: Vec<(&Node<String>, bool)> = layout.root.iter().map(|root| (root, true)).collect();
|
||||
while let Some((node, shortens)) = pending.pop() {
|
||||
match node {
|
||||
Node::Tabs { panels, active } if shortens => out.extend(panels.get(*active).cloned()),
|
||||
Node::Split { axis, first, second, .. } => {
|
||||
let vertical = *axis == craft_ui::layout::SplitAxis::Vertical;
|
||||
pending.extend([(second.as_ref(), vertical), (first.as_ref(), vertical)]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Whether the group of the shared layout's tree holding `panel` as a tab can be shortened to its
|
||||
/// tabs (the whole tree, or above or below another group); `None` when `panel` isn't a tab of one
|
||||
/// (it floats, or is in an accordion).
|
||||
fn tab_group_shortens(layout: &Layout<String>, panel: &str) -> Option<bool> {
|
||||
let mut pending: Vec<(&Node<String>, bool)> = layout.root.iter().map(|root| (root, true)).collect();
|
||||
while let Some((node, shortens)) = pending.pop() {
|
||||
match node {
|
||||
Node::Tabs { panels, .. } if panels.iter().any(|id| id == panel) => return Some(shortens),
|
||||
Node::Split { axis, first, second, .. } => {
|
||||
let vertical = *axis == craft_ui::layout::SplitAxis::Vertical;
|
||||
pending.extend([(second.as_ref(), vertical), (first.as_ref(), vertical)]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Forget the collapsed groups that show another panel now (another tab clicked, or the panel
|
||||
/// moved or closed): those are expanded.
|
||||
pub(crate) fn prune_collapsed(ui: &mut crate::state::UiState) {
|
||||
if ui.docking_collapsed.is_empty() {
|
||||
return;
|
||||
}
|
||||
let shown = dock_groups_shown(ui);
|
||||
ui.docking_collapsed.retain(|id| shown.contains(id));
|
||||
}
|
||||
|
||||
/// `window.panel.collapse`: show the dock group holding `panel` as its tabs only, or expanded.
|
||||
fn collapse(app: &mut VectorcraftApp, params: &Value) -> Result<Value, String> {
|
||||
let panel = params.get("panel").and_then(Value::as_str).and_then(normalize).ok_or("panel must name a registered panel")?;
|
||||
let collapsed = crate::menus::opt_bool(params, "collapsed")?;
|
||||
if crate::floating::group_of(&app.ui, panel).is_some() {
|
||||
return Err("panel floats; collapse its group with window.panel.float {panel, collapsed}".into());
|
||||
}
|
||||
match app.ui.docking.as_mut() {
|
||||
Some(layout) => {
|
||||
match tab_group_shortens(layout, panel) {
|
||||
None => return Err("panel is not in a dock group".into()),
|
||||
Some(false) => return Err("only a dock group above or below others collapses, not one beside another".into()),
|
||||
Some(true) => {}
|
||||
}
|
||||
layout.apply(Action::Activate { panel: panel.into() }).map_err(|e| e.to_string())?;
|
||||
sync_legacy(app);
|
||||
}
|
||||
None => {
|
||||
let tab = crate::state::DockTab::from_id(panel)
|
||||
.filter(|_| crate::floating::docked_tabs(&app.ui).any(|t| t.info().0 == panel))
|
||||
.ok_or("panel is not in a dock group")?;
|
||||
app.ui.dock_tab = tab;
|
||||
}
|
||||
}
|
||||
prune_collapsed(&mut app.ui);
|
||||
let now = collapsed.unwrap_or(!app.ui.docking_collapsed.contains(panel));
|
||||
if now {
|
||||
app.ui.docking_collapsed.insert(panel.into());
|
||||
} else {
|
||||
app.ui.docking_collapsed.remove(panel);
|
||||
}
|
||||
Ok(json!({"panel": panel, "collapsed": now}))
|
||||
}
|
||||
|
||||
/// `node` as drawn with its collapsed groups (showing a panel in `collapsed`) as tall as their
|
||||
/// tabs in a top/bottom split. The paths of the splits so sized go in `fixed`, the panels of
|
||||
/// collapsed groups in `members`. Also returns the node's height when it is only collapsed groups.
|
||||
fn collapse_node(
|
||||
node: &Node<String>,
|
||||
collapsed: &std::collections::BTreeSet<String>,
|
||||
(tab, gap): (f32, f32),
|
||||
path: &mut Vec<bool>,
|
||||
fixed: &mut Vec<Vec<bool>>,
|
||||
members: &mut std::collections::BTreeSet<String>,
|
||||
) -> (Node<String>, Option<f32>) {
|
||||
use craft_ui::layout::{SplitAxis, SplitSize};
|
||||
match node {
|
||||
Node::Tabs { panels, active } if panels.get(*active).is_some_and(|id| collapsed.contains(id)) => {
|
||||
members.extend(panels.iter().cloned());
|
||||
(node.clone(), Some(tab))
|
||||
}
|
||||
Node::Split { axis, size, first, second } => {
|
||||
path.push(false);
|
||||
let (a, ha) = collapse_node(first, collapsed, (tab, gap), path, fixed, members);
|
||||
path.pop();
|
||||
path.push(true);
|
||||
let (b, hb) = collapse_node(second, collapsed, (tab, gap), path, fixed, members);
|
||||
path.pop();
|
||||
let (sized, height) = match (axis, ha, hb) {
|
||||
(SplitAxis::Vertical, Some(ha), Some(hb)) => (Some(SplitSize::FixedFirst(ha)), Some(ha + hb + gap)),
|
||||
(SplitAxis::Vertical, Some(ha), None) => (Some(SplitSize::FixedFirst(ha)), None),
|
||||
(SplitAxis::Vertical, None, Some(hb)) => (Some(SplitSize::FixedSecond(hb)), None),
|
||||
_ => (None, None),
|
||||
};
|
||||
if sized.is_some() {
|
||||
fixed.push(path.clone());
|
||||
}
|
||||
(Node::Split { axis: *axis, size: sized.unwrap_or(*size), first: Box::new(a), second: Box::new(b) }, height)
|
||||
}
|
||||
_ => (node.clone(), None),
|
||||
}
|
||||
}
|
||||
|
||||
fn change(app: &mut VectorcraftApp, operation: &str, params: &Value) -> Result<Value, String> {
|
||||
let panel = params.get("panel").and_then(Value::as_str).and_then(normalize).ok_or("panel must name a registered panel")?;
|
||||
if operation == "move" && params.get("before").is_some() {
|
||||
@@ -584,6 +727,12 @@ fn change(app: &mut VectorcraftApp, operation: &str, params: &Value) -> Result<V
|
||||
app.ui.docking = Some(layout);
|
||||
app.ui.docking_hidden = hidden;
|
||||
app.ui.docking_icons = icons;
|
||||
if operation != "activate" {
|
||||
app.ui.docking_collapsed.remove(panel);
|
||||
for member in &members {
|
||||
app.ui.docking_collapsed.remove(member);
|
||||
}
|
||||
}
|
||||
if operation == "activate" {
|
||||
for group in &mut app.ui.floating_panels {
|
||||
if group.panels.iter().any(|id| id == panel) {
|
||||
@@ -603,10 +752,15 @@ fn change(app: &mut VectorcraftApp, operation: &str, params: &Value) -> Result<V
|
||||
struct PanelContent<'a> {
|
||||
app: &'a mut VectorcraftApp,
|
||||
commands: Vec<(&'static str, Value)>,
|
||||
/// The panels of groups collapsed to their tabs: no body.
|
||||
shut: std::collections::BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
fn body(&mut self, ui: &mut egui::Ui, panel: &String) {
|
||||
if self.shut.contains(panel) {
|
||||
return;
|
||||
}
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
let back = ui.max_rect();
|
||||
if back.is_positive() {
|
||||
@@ -654,6 +808,7 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
panel: active.clone(),
|
||||
layout: self.app.ui.docking.clone().unwrap_or_else(|| legacy(self.app)),
|
||||
hidden: self.app.ui.docking_hidden.clone(),
|
||||
collapsed: self.app.ui.docking_collapsed.clone(),
|
||||
open_panel: self.app.ui.open_panel.clone(),
|
||||
generation: self.app.ui.docking_generation,
|
||||
},
|
||||
@@ -742,6 +897,7 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
} else if ui.input(|input| !input.focused || input.key_pressed(egui::Key::Escape)) {
|
||||
app.ui.docking = Some(drag.layout);
|
||||
app.ui.docking_hidden = drag.hidden;
|
||||
app.ui.docking_collapsed = drag.collapsed;
|
||||
app.ui.open_panel = drag.open_panel;
|
||||
sync_legacy(app);
|
||||
ui.ctx().data_mut(|data| data.remove::<RootGroupDrag>(root_key));
|
||||
@@ -820,6 +976,8 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
.collect();
|
||||
crate::dock::apply_pending_dock_width(app, ui.ctx(), crate::dock::SHARED_DOCK_PANEL, crate::dock::clamp_shared_dock_width);
|
||||
let shared_dock_default = crate::dock::shared_dock_default_width(&app.ui);
|
||||
// Splits sized to a collapsed group's tabs: their dividers don't resize the saved layout.
|
||||
let mut fixed: Vec<Vec<bool>> = Vec::new();
|
||||
let mut draw = |ui: &mut egui::Ui| {
|
||||
if layout.root.is_some() && crate::dock::collapse_header(ui, collapsed) {
|
||||
let _ = app.run("window.collapseDock", json!({"collapsed":!collapsed}));
|
||||
@@ -862,23 +1020,60 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
style.panels_label = tl!("Panels").to_string();
|
||||
style.resize_label = tl!("Resize panels").to_string();
|
||||
style.resize_window_label = tl!("Resize panel window").to_string();
|
||||
let tree = ui.available_rect_before_wrap();
|
||||
let mut content = PanelContent { app, commands: Vec::new() };
|
||||
let output = craft_ui::docking::DockArea::new(area).show_customized(
|
||||
ui,
|
||||
&display,
|
||||
&style,
|
||||
|id| {
|
||||
crate::state::all_panels()
|
||||
.find(|p| p.0 == id)
|
||||
.map(|p| tl!(p.1).to_string())
|
||||
.or_else(|| library_label(id))
|
||||
.unwrap_or_else(|| id.clone())
|
||||
},
|
||||
|_| craft_ui::docking::Permissions::default(),
|
||||
|_| craft_ui::docking::PanelLimits { min: egui::vec2(220.0, 80.0), ..Default::default() },
|
||||
&mut content,
|
||||
);
|
||||
// Panels set their own minimum height, so a collapsed group can be as short as its tabs.
|
||||
style.min_pane = 0.0;
|
||||
let mut tree = ui.available_rect_before_wrap();
|
||||
let mut shut = std::collections::BTreeSet::new();
|
||||
let mut drawn = display.clone();
|
||||
// The height of the tree when it is only collapsed groups: drawn that tall, so no group
|
||||
// takes the rest of the dock as a blank pane.
|
||||
let mut only_tabs = None;
|
||||
if let Some(root) = &display.root {
|
||||
let (node, height) =
|
||||
collapse_node(root, &app.ui.docking_collapsed, (style.tab_height, style.gap), &mut Vec::new(), &mut fixed, &mut shut);
|
||||
drawn.root = Some(node);
|
||||
only_tabs = height.filter(|h| h.is_finite() && *h > 0.0);
|
||||
}
|
||||
let short = shut.clone();
|
||||
let mut content = PanelContent { app, commands: Vec::new(), shut };
|
||||
let mut dock = |ui: &mut egui::Ui| {
|
||||
craft_ui::docking::DockArea::new(area).show_customized(
|
||||
ui,
|
||||
&drawn,
|
||||
&style,
|
||||
|id| {
|
||||
crate::state::all_panels()
|
||||
.find(|p| p.0 == id)
|
||||
.map(|p| tl!(p.1).to_string())
|
||||
.or_else(|| library_label(id))
|
||||
.unwrap_or_else(|| id.clone())
|
||||
},
|
||||
|_| craft_ui::docking::Permissions::default(),
|
||||
|id| craft_ui::docking::PanelLimits { min: egui::vec2(220.0, if short.contains(id) { 0.0 } else { 80.0 }), ..Default::default() },
|
||||
&mut content,
|
||||
)
|
||||
};
|
||||
let output = match only_tabs {
|
||||
Some(height) => {
|
||||
let size = egui::vec2(ui.available_width(), height.min(ui.available_height()));
|
||||
ui.allocate_ui(size, |ui| {
|
||||
ui.set_max_height(size.y);
|
||||
tree = ui.available_rect_before_wrap();
|
||||
dock(ui)
|
||||
})
|
||||
.inner
|
||||
}
|
||||
None => dock(ui),
|
||||
};
|
||||
// A double-click on a tab of the dock's tree collapses its group to its tabs, or expands it.
|
||||
if let Some(root) = &drawn.root {
|
||||
let tabs = Layout { root: Some(root.clone()), floating: Vec::new() };
|
||||
if let Some(panel) =
|
||||
tabs.panels().into_iter().find(|p| ui.ctx().read_response(area.with(("tab", *p))).is_some_and(|r| r.double_clicked()))
|
||||
{
|
||||
content.commands.push(("window.panel.collapse", json!({"panel": panel})));
|
||||
}
|
||||
}
|
||||
let groups = if collapsed { Vec::new() } else { root_groups(ui.ctx(), &display, tree, style.tab_height) };
|
||||
ui.ctx().data_mut(|data| data.insert_temp(crate::floating::root_groups_id(), groups));
|
||||
for (id, params) in content.commands {
|
||||
@@ -950,6 +1145,9 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
ui.ctx().data_mut(|data| data.remove::<LegacyDrag>(egui::Id::new("vectorcraft-panel-docking").with("legacy-origin")));
|
||||
}
|
||||
for action in output.actions {
|
||||
if matches!(&action, Action::ResizeSplit { path, .. } if fixed.contains(path)) {
|
||||
continue;
|
||||
}
|
||||
if tab_drop
|
||||
.is_some_and(|(panel, _)| matches!(&action, Action::Float { panel: moved, .. } | Action::Move { panel: moved, .. } if moved == panel))
|
||||
{
|
||||
@@ -1129,6 +1327,7 @@ pub(crate) fn legacy_tab_in_strip(
|
||||
open_panel: app.ui.open_panel.clone(),
|
||||
floating: app.ui.floating_panels.clone(),
|
||||
icons: app.ui.docking_icons.clone(),
|
||||
collapsed: app.ui.docking_collapsed.clone(),
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
@@ -919,3 +919,214 @@ fn a_panel_the_icon_column_has_no_place_for_still_shows_there_once_dropped_on_it
|
||||
let column = h.ctx.data(|d| d.get_temp::<egui::Rect>(crate::floating::icons_rect_id())).unwrap();
|
||||
assert!(h.query_all_by_label("Character Styles").any(|node| column.contains_rect(node.rect())), "its icon is in the column");
|
||||
}
|
||||
|
||||
/// Double-clicks at `at`, then waits out the double-click time.
|
||||
fn double_click(h: &mut Harness<'static, VectorcraftApp>, at: Pos2) {
|
||||
h.event(Event::PointerMoved(at));
|
||||
h.run_steps(1);
|
||||
for _ in 0..2 {
|
||||
h.event(Event::PointerButton { pos: at, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerButton { pos: at, button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
}
|
||||
// Past the double- and triple-click times, so the next press is a click of its own.
|
||||
h.run_steps(45);
|
||||
}
|
||||
|
||||
/// The Layers panel's body is drawn (its search field).
|
||||
fn layers_drawn(h: &Harness<'static, VectorcraftApp>) -> bool {
|
||||
h.ctx.read_response(egui::Id::new("layers-search").with("edit")).is_some()
|
||||
}
|
||||
|
||||
/// Clicks at `at`.
|
||||
fn click(h: &mut Harness<'static, VectorcraftApp>, at: Pos2) {
|
||||
h.event(Event::PointerMoved(at));
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerButton { pos: at, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerButton { pos: at, button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(3);
|
||||
}
|
||||
|
||||
/// The middle of `panel`'s tab in the shared dock.
|
||||
/// Two stacked groups both collapsed are both as short as their tabs: the lower one doesn't take
|
||||
/// the rest of the dock as a blank pane.
|
||||
#[test]
|
||||
fn two_stacked_groups_both_collapsed_are_both_as_short_as_their_tabs() {
|
||||
let mut app = app();
|
||||
app.run("window.panel.move", json!({"panel":"swatches", "anchor":"layers", "zone":"top"})).unwrap();
|
||||
app.run("window.panel.collapse", json!({"panel":"swatches", "collapsed": true})).unwrap();
|
||||
app.run("window.panel.collapse", json!({"panel":"layers", "collapsed": true})).unwrap();
|
||||
let h = harness(app, vec2(1280.0, 800.0));
|
||||
let groups = root_groups(&h);
|
||||
assert_eq!(groups.len(), 2, "{groups:?}");
|
||||
for group in &groups {
|
||||
assert!(group.rect.height() < 40.0, "{groups:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_center(h: &Harness<'static, VectorcraftApp>, panel: &str) -> Pos2 {
|
||||
h.ctx.read_response(egui::Id::new("vectorcraft-panel-docking").with(("tab", &panel.to_string()))).unwrap().rect.center()
|
||||
}
|
||||
|
||||
/// A double-click on a tab of the shared dock collapses its group to its tabs; another tab expands it.
|
||||
#[test]
|
||||
fn a_double_click_on_a_dock_tab_collapses_its_group_to_its_tabs_and_another_expands_it() {
|
||||
let mut app = app();
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
app.run("window.panel.move", json!({"panel":"swatches", "anchor":"layers", "zone":"top"})).unwrap();
|
||||
let saved = app.ui.docking.clone();
|
||||
let mut h = harness(app, vec2(1280.0, 800.0));
|
||||
let groups = root_groups(&h);
|
||||
assert_eq!(groups.len(), 2, "{groups:?}");
|
||||
let (top, lower) = (groups[0].rect, groups[1].rect);
|
||||
assert!(top.height() > 100.0, "{top:?}");
|
||||
|
||||
// The upper group shrinks to its tabs and the lower one takes the room; the saved layout keeps
|
||||
// its sizes.
|
||||
let at = tab_center(&h, "swatches");
|
||||
double_click(&mut h, at);
|
||||
assert!(h.state().ui.docking_collapsed.contains("swatches"));
|
||||
let groups = root_groups(&h);
|
||||
assert!(groups[0].rect.height() < 40.0, "{groups:?}");
|
||||
assert!(groups[1].rect.top() < lower.top() - 50.0, "{groups:?}");
|
||||
assert_eq!(h.state().ui.docking, saved);
|
||||
// Again, and it is as tall as before.
|
||||
let at = tab_center(&h, "swatches");
|
||||
double_click(&mut h, at);
|
||||
assert!(h.state().ui.docking_collapsed.is_empty());
|
||||
let groups = root_groups(&h);
|
||||
assert!((groups[0].rect.height() - top.height()).abs() < 2.0, "{groups:?} {top:?}");
|
||||
|
||||
// The lower group, tabbed: its body goes, and a click on another of its tabs brings it back
|
||||
// showing that panel.
|
||||
let at = tab_center(&h, "layers");
|
||||
double_click(&mut h, at);
|
||||
assert!(h.state().ui.docking_collapsed.contains("layers"));
|
||||
assert!(!layers_drawn(&h), "no Layers body");
|
||||
let at = tab_center(&h, "properties");
|
||||
click(&mut h, at);
|
||||
assert!(h.state().ui.docking_collapsed.is_empty());
|
||||
assert_eq!(root_group_of(h.state(), "properties"), ["properties", "layers", "libraries"]);
|
||||
assert!(valid(h.state().ui.docking.as_ref().unwrap()));
|
||||
}
|
||||
|
||||
/// A double-click on a tab of the legacy dock collapses the tabbed group to its tabs.
|
||||
#[test]
|
||||
fn a_double_click_on_a_legacy_dock_tab_collapses_the_tabbed_group() {
|
||||
let mut app = app();
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
let mut h = harness(app, vec2(1280.0, 800.0));
|
||||
let tab = h.query_all_by_label("Layers").find(|node| node.rect().height() <= 45.0).unwrap().rect().center();
|
||||
click(&mut h, tab);
|
||||
h.run_steps(45);
|
||||
assert!(layers_drawn(&h), "Layers shows");
|
||||
double_click(&mut h, tab);
|
||||
assert!(h.state().ui.docking.is_none(), "still the legacy dock");
|
||||
assert!(h.state().ui.docking_collapsed.contains("layers"));
|
||||
assert!(!layers_drawn(&h), "no Layers body");
|
||||
double_click(&mut h, tab);
|
||||
assert!(h.state().ui.docking_collapsed.is_empty());
|
||||
assert!(layers_drawn(&h), "Layers shows again");
|
||||
}
|
||||
|
||||
/// `window.panel.collapse` checks its parameters, and a collapsed group survives a reload.
|
||||
#[test]
|
||||
fn the_collapse_command_validates_and_survives_a_reload() {
|
||||
let mut app = app();
|
||||
assert_eq!(app.run("window.panel.collapse", json!({"panel":"layers"})).unwrap(), json!({"panel":"layers", "collapsed":true}));
|
||||
assert_eq!(app.ui.dock_tab, crate::state::DockTab::Layers, "it shows the panel");
|
||||
let saved = serde_json::to_value(&app.ui).unwrap();
|
||||
let restored = serde_json::from_value::<crate::state::UiState>(saved).unwrap().sanitized();
|
||||
assert!(restored.docking_collapsed.contains("layers"));
|
||||
assert_eq!(app.run("window.panel.collapse", json!({"panel":"layers", "collapsed":false})).unwrap()["collapsed"], false);
|
||||
for params in [json!({"panel":"nope"}), json!({"panel":"layers", "collapsed":"yes"}), json!({"panel":"swatches"})] {
|
||||
assert!(app.run("window.panel.collapse", params.clone()).is_err(), "{params}");
|
||||
}
|
||||
app.run("window.panel.float", json!({"panel":"layers", "x":300, "y":300})).unwrap();
|
||||
assert!(app.run("window.panel.collapse", json!({"panel":"layers"})).is_err(), "floating groups collapse with float");
|
||||
assert!(app.ui.docking_collapsed.is_empty());
|
||||
}
|
||||
|
||||
/// A panel moved out of a collapsed group doesn't take the collapse along.
|
||||
#[test]
|
||||
fn a_collapsed_groups_panel_moved_elsewhere_takes_no_collapse_along() {
|
||||
let mut app = app();
|
||||
app.run("window.panel.move", json!({"panel":"swatches", "anchor":"layers", "zone":"top"})).unwrap();
|
||||
app.run("window.panel.collapse", json!({"panel":"layers"})).unwrap();
|
||||
// Layers, shown by the collapsed group, goes to the Swatches group and is shown there.
|
||||
app.run("window.panel.move", json!({"panel":"layers", "anchor":"swatches"})).unwrap();
|
||||
app.run("window.panel.activate", json!({"panel":"layers"})).unwrap();
|
||||
crate::panel_docking::prune_collapsed(&mut app.ui);
|
||||
assert!(app.ui.docking_collapsed.is_empty(), "neither group is collapsed");
|
||||
// The same through a serialized action (a drag).
|
||||
app.run("window.panel.collapse", json!({"panel":"layers"})).unwrap();
|
||||
app.run("window.panel.layout", json!({"action": {"Move": {"panel":"layers", "anchor":"properties", "placement": {"Tab": {"before": null}}}}}))
|
||||
.unwrap();
|
||||
assert!(app.ui.docking_collapsed.is_empty());
|
||||
}
|
||||
|
||||
/// `window.panel.collapse` refuses a panel that is in an accordion, not a tab group.
|
||||
#[test]
|
||||
fn the_collapse_command_rejects_an_accordion_panel() {
|
||||
let mut app = app();
|
||||
app.ui.docking = Some(Layout {
|
||||
root: Some(Node::Stack {
|
||||
entries: vec![
|
||||
craft_ui::docking::StackEntry { panel: "layers".into(), open: true, height: None },
|
||||
craft_ui::docking::StackEntry { panel: "swatches".into(), open: true, height: None },
|
||||
],
|
||||
}),
|
||||
floating: Vec::new(),
|
||||
});
|
||||
assert!(valid(app.ui.docking.as_ref().unwrap()));
|
||||
assert!(app.run("window.panel.collapse", json!({"panel":"layers"})).is_err());
|
||||
assert!(app.ui.docking_collapsed.is_empty());
|
||||
}
|
||||
|
||||
/// Escape on a collapsed group's drag puts it back collapsed.
|
||||
#[test]
|
||||
fn escape_on_a_collapsed_groups_drag_leaves_it_collapsed() {
|
||||
let mut app = app();
|
||||
app.run("window.panel.move", json!({"panel":"swatches", "anchor":"layers", "zone":"top"})).unwrap();
|
||||
app.run("window.panel.collapse", json!({"panel":"layers"})).unwrap();
|
||||
let mut h = harness(app, vec2(1280.0, 800.0));
|
||||
// An icon panel's flyout is open too.
|
||||
h.state_mut().ui.open_panel = Some("color".into());
|
||||
h.run_steps(2);
|
||||
let groups = root_groups(&h);
|
||||
let lower = groups.iter().find(|g| g.panels.contains(&"layers")).unwrap().strip;
|
||||
// The grip right of the lower group's tabs, dragged out of the dock, then Escape.
|
||||
let from = lower.right_center() - vec2(50.0, 0.0);
|
||||
h.event(Event::PointerMoved(from));
|
||||
h.run_steps(1);
|
||||
h.event(Event::PointerButton { pos: from, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
for step in 1..=6 {
|
||||
h.event(Event::PointerMoved(from - vec2(80.0 * step as f32, 0.0)));
|
||||
h.run_steps(1);
|
||||
}
|
||||
assert!(h.state().ui.docking.as_ref().unwrap().floating.iter().any(|g| g.panels.contains(&"layers".into())), "the group floats mid-drag");
|
||||
h.event(Event::Key { key: egui::Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE });
|
||||
h.run_steps(3);
|
||||
assert!(h.state().ui.docking.as_ref().unwrap().floating.is_empty(), "back in the dock");
|
||||
assert!(h.state().ui.docking_collapsed.contains("layers"), "and still collapsed");
|
||||
assert_eq!(h.state().ui.open_panel.as_deref(), Some("color"), "the flyout is back");
|
||||
}
|
||||
|
||||
/// A group beside another (a left/right split) is refused: it can't be shorter than its neighbour.
|
||||
#[test]
|
||||
fn a_group_beside_another_does_not_collapse() {
|
||||
let mut app = app();
|
||||
app.run("window.panel.move", json!({"panel":"swatches", "anchor":"layers", "zone":"left"})).unwrap();
|
||||
let before = app.ui.docking.clone();
|
||||
assert!(app.run("window.panel.collapse", json!({"panel":"swatches"})).is_err());
|
||||
assert!(app.run("window.panel.collapse", json!({"panel":"properties"})).is_err());
|
||||
assert_eq!(app.ui.docking, before, "refused without a change");
|
||||
assert!(app.ui.docking_collapsed.is_empty());
|
||||
// One left collapsed by an earlier layout (a hand-edited file) is expanded.
|
||||
app.ui.docking_collapsed.insert("swatches".into());
|
||||
crate::panel_docking::prune_collapsed(&mut app.ui);
|
||||
assert!(app.ui.docking_collapsed.is_empty());
|
||||
}
|
||||
|
||||
@@ -369,6 +369,10 @@ pub struct UiState {
|
||||
/// Panels whose saved return destination is the iconic rail.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
|
||||
pub docking_icons: std::collections::BTreeSet<String>,
|
||||
/// The panels shown by dock groups collapsed to their tabs (a double-click on a tab); a group
|
||||
/// showing another of its panels is expanded.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
|
||||
pub docking_collapsed: std::collections::BTreeSet<String>,
|
||||
/// Invalidates in-flight gestures when a workspace is replaced.
|
||||
#[serde(skip)]
|
||||
pub docking_generation: u64,
|
||||
@@ -782,6 +786,11 @@ impl UiState {
|
||||
self.docking = self.docking.filter(crate::panel_docking::valid);
|
||||
// The tabbed group's panels live in the icon column too once dropped there.
|
||||
self.docking_icons.retain(|id| rail_panel(id).is_some());
|
||||
self.docking_collapsed = std::mem::take(&mut self.docking_collapsed)
|
||||
.into_iter()
|
||||
.filter(|id| crate::menus::normalize_panel(id) == Some(id.as_str()))
|
||||
.take(64)
|
||||
.collect();
|
||||
// Changes kept for workspaces that still exist, under their saved spelling and one per
|
||||
// workspace whatever the case (a hand-edited file).
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
@@ -825,6 +834,7 @@ impl Default for UiState {
|
||||
docking: None,
|
||||
docking_hidden: Default::default(),
|
||||
docking_icons: Default::default(),
|
||||
docking_collapsed: Default::default(),
|
||||
docking_generation: 0,
|
||||
dock_width: None,
|
||||
dock_width_pending: false,
|
||||
|
||||
@@ -44,6 +44,9 @@ pub struct Workspace {
|
||||
/// Panels whose saved return destination is the iconic rail.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
|
||||
pub docking_icons: std::collections::BTreeSet<String>,
|
||||
/// The panels shown by dock groups collapsed to their tabs.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
|
||||
pub docking_collapsed: std::collections::BTreeSet<String>,
|
||||
/// Where the Tools panel floats (built-in workspaces: docked).
|
||||
pub toolbar_pos: Option<[f32; 2]>,
|
||||
/// Main dock column width in screen points (#1753).
|
||||
@@ -180,6 +183,7 @@ pub fn capture(ui: &UiState, name: &str) -> Workspace {
|
||||
docking: ui.docking.clone(),
|
||||
docking_hidden: ui.docking_hidden.clone(),
|
||||
docking_icons: ui.docking_icons.clone(),
|
||||
docking_collapsed: ui.docking_collapsed.clone(),
|
||||
toolbar_pos: ui.toolbar_pos,
|
||||
dock_width: ui.dock_width,
|
||||
}
|
||||
@@ -205,6 +209,7 @@ pub fn apply(ui: &mut UiState, w: &Workspace) {
|
||||
ui.docking = w.docking.clone().filter(crate::panel_docking::valid);
|
||||
ui.docking_hidden = w.docking_hidden.clone();
|
||||
ui.docking_icons = w.docking_icons.iter().filter(|id| crate::state::rail_panel(id).is_some()).take(64).cloned().collect();
|
||||
ui.docking_collapsed = w.docking_collapsed.iter().filter(|id| crate::menus::normalize_panel(id) == Some(id.as_str())).take(64).cloned().collect();
|
||||
ui.toolbar_pos = w.toolbar_pos;
|
||||
ui.dock_width = w.dock_width;
|
||||
ui.dock_width_pending = true;
|
||||
@@ -557,12 +562,14 @@ mod tests {
|
||||
// Changes made in it after saving (a panel moved, the control bar shown)…
|
||||
ui.control_bar = true;
|
||||
ui.dock_tab = DockTab::Layers;
|
||||
ui.docking_icons.insert("color".into());
|
||||
ui.docking_icons.insert("layers".into());
|
||||
ui.docking_collapsed.insert("layers".into());
|
||||
// …are still there after switching away and back.
|
||||
switch(&mut ui, ESSENTIALS).unwrap();
|
||||
assert!(!ui.control_bar && ui.docking_icons.is_empty(), "Essentials comes as saved");
|
||||
assert!(!ui.control_bar && ui.docking_icons.is_empty() && ui.docking_collapsed.is_empty(), "Essentials comes as saved");
|
||||
switch(&mut ui, "Görkem").unwrap();
|
||||
assert!(ui.control_bar && ui.dock_tab == DockTab::Layers && ui.docking_icons.contains("color"));
|
||||
assert!(ui.control_bar && ui.dock_tab == DockTab::Layers && ui.docking_icons.contains("layers"));
|
||||
assert!(ui.docking_collapsed.contains("layers"), "its collapsed group too");
|
||||
// Built-in workspaces remember theirs too.
|
||||
switch(&mut ui, ESSENTIALS).unwrap();
|
||||
ui.status_bar = false;
|
||||
|
||||
@@ -858,6 +858,11 @@ positions of hidden panels. Resetting a built-in workspace restores its default
|
||||
dock placement when possible, or to the supplied destination.
|
||||
- `window.panel.activate {panel}` reveals and selects a panel, restoring its saved
|
||||
position when possible. `window.panel.close {panel}` hides it.
|
||||
- `window.panel.collapse {panel, collapsed?}` shows the dock group holding a panel as its tabs
|
||||
only, as a double-click on a tab does (without `collapsed` it toggles), and makes the panel the
|
||||
group's shown one; showing another of its panels expands the group. Only a group that is the
|
||||
whole dock or sits above or below another collapses (one beside another is refused). Floating
|
||||
groups collapse with `window.panel.float {panel, collapsed}`. Returns `{panel, collapsed}`.
|
||||
|
||||
Panel IDs are stable across themes. Invalid IDs, non-finite or invalid geometry, and
|
||||
invalid destinations return an error without changing the arrangement.
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user