Merge pull request #3281 from gorkematlyn/upstream-ui8

UI.8: collapse dock groups to their tabs
This commit is contained in:
BFlat
2026-10-22 04:13:54 +07:00
committed by GitHub
34 changed files with 572 additions and 28 deletions
+12 -3
View File
@@ -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;
}
+1
View File
@@ -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;
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+4
View File
@@ -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.
+6
View File
@@ -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",
+216 -17
View File
@@ -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(),
},
)
});
+211
View File
@@ -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());
}
+10
View File
@@ -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,
+10 -3
View File
@@ -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;
+5
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -1
View File
File diff suppressed because one or more lines are too long