forked from 1b-agent/opencadstudio-upstream
Merge commit 'ac9a6b07' into codex/pointcloud-attach
# Conflicts: # locales/ar-SA/opencadstudio.ftl # locales/bg-BG/opencadstudio.ftl # locales/cs-CZ/opencadstudio.ftl # locales/de-DE/opencadstudio.ftl # locales/el-GR/opencadstudio.ftl # locales/en-US/opencadstudio.ftl # locales/es-ES/opencadstudio.ftl # locales/fi-FI/opencadstudio.ftl # locales/fr-FR/opencadstudio.ftl # locales/hi-IN/opencadstudio.ftl # locales/hu-HU/opencadstudio.ftl # locales/it-IT/opencadstudio.ftl # locales/ja-JP/opencadstudio.ftl # locales/ko-KR/opencadstudio.ftl # locales/nl-NL/opencadstudio.ftl # locales/pl-PL/opencadstudio.ftl # locales/pt-BR/opencadstudio.ftl # locales/ru-RU/opencadstudio.ftl # locales/tr-TR/opencadstudio.ftl # locales/zh-CN/opencadstudio.ftl # locales/zh-TW/opencadstudio.ftl # src/app/control/actions.rs # src/locale_catalog.rs
This commit is contained in:
commit
ee0d4aedb5
35 files changed
+673
-13
No files matched your search
@@ -4003,6 +4003,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = إنشاء
|
.create = إنشاء
|
||||||
.boolean = عمليات منطقية
|
.boolean = عمليات منطقية
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Създаване
|
.create = Създаване
|
||||||
.boolean = Булеви операции
|
.boolean = Булеви операции
|
||||||
|
|||||||
@@ -3988,6 +3988,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Vytvořit
|
.create = Vytvořit
|
||||||
.boolean = Booleovské operace
|
.boolean = Booleovské operace
|
||||||
|
|||||||
@@ -3989,6 +3989,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Erstellen
|
.create = Erstellen
|
||||||
.boolean = Boolesche Operationen
|
.boolean = Boolesche Operationen
|
||||||
|
|||||||
@@ -3991,6 +3991,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Δημιουργία
|
.create = Δημιουργία
|
||||||
.boolean = Δυαδική πράξη
|
.boolean = Δυαδική πράξη
|
||||||
|
|||||||
@@ -3992,6 +3992,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Create
|
.create = Create
|
||||||
.boolean = Boolean
|
.boolean = Boolean
|
||||||
|
|||||||
@@ -3991,6 +3991,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Crear
|
.create = Crear
|
||||||
.boolean = Booleanas
|
.boolean = Booleanas
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Luo
|
.create = Luo
|
||||||
.boolean = Boolen operaatiot
|
.boolean = Boolen operaatiot
|
||||||
|
|||||||
@@ -3991,6 +3991,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Créer
|
.create = Créer
|
||||||
.boolean = Opérations booléennes
|
.boolean = Opérations booléennes
|
||||||
|
|||||||
@@ -3979,6 +3979,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = बनाएँ
|
.create = बनाएँ
|
||||||
.boolean = बूलियन संक्रियाएँ
|
.boolean = बूलियन संक्रियाएँ
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Létrehozás
|
.create = Létrehozás
|
||||||
.boolean = Logikai műveletek
|
.boolean = Logikai műveletek
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Crea
|
.create = Crea
|
||||||
.boolean = Operazioni booleane
|
.boolean = Operazioni booleane
|
||||||
|
|||||||
@@ -3979,6 +3979,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = 作成
|
.create = 作成
|
||||||
.boolean = ブール演算
|
.boolean = ブール演算
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = 생성
|
.create = 생성
|
||||||
.boolean = 불리언 연산
|
.boolean = 불리언 연산
|
||||||
|
|||||||
@@ -3989,6 +3989,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Maken
|
.create = Maken
|
||||||
.boolean = Booleaanse bewerkingen
|
.boolean = Booleaanse bewerkingen
|
||||||
|
|||||||
@@ -3990,6 +3990,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Utwórz
|
.create = Utwórz
|
||||||
.boolean = Operacje boolowskie
|
.boolean = Operacje boolowskie
|
||||||
|
|||||||
@@ -3991,6 +3991,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Criar
|
.create = Criar
|
||||||
.boolean = Operações booleanas
|
.boolean = Operações booleanas
|
||||||
|
|||||||
@@ -3992,6 +3992,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = Создание
|
.create = Создание
|
||||||
.boolean = Булевы операции
|
.boolean = Булевы операции
|
||||||
|
|||||||
@@ -3951,6 +3951,14 @@ insert =
|
|||||||
.pcm-name-taken = Bu adda bir renk şeması zaten var.
|
.pcm-name-taken = Bu adda bir renk şeması zaten var.
|
||||||
.pcm-bad-range = Renklendirilen nokta aralığı geçersiz.
|
.pcm-bad-range = Renklendirilen nokta aralığı geçersiz.
|
||||||
.pcm-help = Nokta bulutunun yoğunluğa veya yüksekliğe göre renklendirildiği renk şemalarını ve aralıkları ayarlar.
|
.pcm-help = Nokta bulutunun yoğunluğa veya yüksekliğe göre renklendirildiği renk şemalarını ve aralıkları ayarlar.
|
||||||
|
.pc-manager = Nokta Bulutu Yöneticisi
|
||||||
|
.pc-regions-scans = BÖLGELER VE TARAMALAR
|
||||||
|
.pc-regions = Bölgeler
|
||||||
|
.pc-unassigned = Atanmamış Noktalar
|
||||||
|
.pc-scans = Taramalar
|
||||||
|
.pc-search = Ara
|
||||||
|
.pc-collapse-all = Tümünü daralt
|
||||||
|
.pc-expand-all = Tümünü genişlet
|
||||||
model =
|
model =
|
||||||
.create = Oluştur
|
.create = Oluştur
|
||||||
.boolean = Boole işlemleri
|
.boolean = Boole işlemleri
|
||||||
|
|||||||
@@ -3974,6 +3974,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = 创建
|
.create = 创建
|
||||||
.boolean = 布尔运算
|
.boolean = 布尔运算
|
||||||
|
|||||||
@@ -3986,6 +3986,14 @@ insert =
|
|||||||
.pcm-name-taken = A color scheme with this name already exists.
|
.pcm-name-taken = A color scheme with this name already exists.
|
||||||
.pcm-bad-range = Invalid range of colorized points.
|
.pcm-bad-range = Invalid range of colorized points.
|
||||||
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
.pcm-help = Sets the color schemes and ranges a point cloud is colored by intensity or elevation.
|
||||||
|
.pc-manager = Point Cloud Manager
|
||||||
|
.pc-regions-scans = REGIONS AND SCANS
|
||||||
|
.pc-regions = Regions
|
||||||
|
.pc-unassigned = Unassigned Points
|
||||||
|
.pc-scans = Scans
|
||||||
|
.pc-search = Search
|
||||||
|
.pc-collapse-all = Collapse all
|
||||||
|
.pc-expand-all = Expand all
|
||||||
model =
|
model =
|
||||||
.create = 建立
|
.create = 建立
|
||||||
.boolean = 布林運算
|
.boolean = 布林運算
|
||||||
|
|||||||
@@ -740,6 +740,8 @@ inventory::submit!(crate::command::CommandRegistration {
|
|||||||
"EXPORTSTEP",
|
"EXPORTSTEP",
|
||||||
"EXPORTSTL",
|
"EXPORTSTL",
|
||||||
"EXTERNALREFERENCES",
|
"EXTERNALREFERENCES",
|
||||||
|
"POINTCLOUDMANAGER",
|
||||||
|
"POINTCLOUDMANAGERCLOSE",
|
||||||
"EXTRIM",
|
"EXTRIM",
|
||||||
"FILETAB",
|
"FILETAB",
|
||||||
"FIND",
|
"FIND",
|
||||||
|
|||||||
@@ -34,7 +34,13 @@ impl OpenCADStudio {
|
|||||||
common.color.clone()
|
common.color.clone()
|
||||||
};
|
};
|
||||||
let color = color.rgb().map_or([255; 3], |(r, g, b)| [r, g, b]);
|
let color = color.rgb().map_or([255; 3], |(r, g, b)| [r, g, b]);
|
||||||
if let Some(placed) = crate::scene::model::point_cloud::placed(document, data, color, None) {
|
if let Some(placed) = crate::scene::model::point_cloud::placed(
|
||||||
|
document,
|
||||||
|
data,
|
||||||
|
color,
|
||||||
|
None,
|
||||||
|
&crate::scene::model::point_cloud::hidden(common),
|
||||||
|
) {
|
||||||
clouds.push((common.handle, placed));
|
clouds.push((common.handle, placed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,59 @@ impl OpenCADStudio {
|
|||||||
self.refresh_properties();
|
self.refresh_properties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Point Cloud Manager click: a switch writes the cloud's visibility
|
||||||
|
/// extended data as one undo step; the rest is palette state.
|
||||||
|
pub(in crate::app) fn update_pc_manager(&mut self, message: crate::ui::window::pc_manager::PcManagerMsg) -> Task<Message> {
|
||||||
|
use crate::scene::model::point_cloud::VISIBILITY_APP;
|
||||||
|
use crate::ui::window::pc_manager::{self, PcManagerMsg};
|
||||||
|
let i = self.active_tab;
|
||||||
|
match message {
|
||||||
|
PcManagerMsg::Toggle(handle, row) => {
|
||||||
|
let Some(cloud) =
|
||||||
|
pc_manager::clouds(&self.tabs[i].scene.document, &[]).into_iter().find(|c| c.handle == handle)
|
||||||
|
else {
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
if self.tabs[i].scene.is_layer_locked(handle) {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
let hidden = pc_manager::toggled(&cloud.hidden, &cloud.scans, &row);
|
||||||
|
self.push_undo_snapshot(i, "POINTCLOUDMANAGER");
|
||||||
|
self.tabs[i].scene.ensure_app_id(VISIBILITY_APP);
|
||||||
|
let app_handle = self.tabs[i].scene.document.app_ids.get(VISIBILITY_APP).map(|a| a.handle.value());
|
||||||
|
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
let data = &mut entity.common_mut().extended_data;
|
||||||
|
if let Some(app) = app_handle {
|
||||||
|
data.raw_dwg_eed.retain(|(a, _)| *a != app);
|
||||||
|
}
|
||||||
|
data.remove_record(VISIBILITY_APP);
|
||||||
|
if !hidden.is_empty() {
|
||||||
|
let mut record = codec::xdata::ExtendedDataRecord::new(VISIBILITY_APP);
|
||||||
|
for name in hidden {
|
||||||
|
record.add_value(codec::xdata::XDataValue::String(name));
|
||||||
|
}
|
||||||
|
data.add_record(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.tabs[i].scene.bump_entities(&[(handle, crate::scene::ChangeKind::Modified)]);
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
}
|
||||||
|
PcManagerMsg::Expand(key) => {
|
||||||
|
if !self.pc_manager.collapsed.remove(&key) {
|
||||||
|
self.pc_manager.collapsed.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PcManagerMsg::Select(key) => self.pc_manager.selected = Some(key),
|
||||||
|
PcManagerMsg::Search(text) => self.pc_manager.search = text,
|
||||||
|
PcManagerMsg::CollapseAll => {
|
||||||
|
let clouds = pc_manager::clouds(&self.tabs[i].scene.document, &[]);
|
||||||
|
self.pc_manager.collapsed.extend(pc_manager::folding_keys(&clouds));
|
||||||
|
}
|
||||||
|
PcManagerMsg::ExpandAll => self.pc_manager.collapsed.clear(),
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
/// One undo step that edits every selected PDF underlay.
|
/// One undo step that edits every selected PDF underlay.
|
||||||
fn edit_selected_underlays(&mut self, i: usize, label: &str, edit: impl Fn(&mut Underlay)) {
|
fn edit_selected_underlays(&mut self, i: usize, label: &str, edit: impl Fn(&mut Underlay)) {
|
||||||
let handles: Vec<_> = self
|
let handles: Vec<_> = self
|
||||||
@@ -319,7 +372,23 @@ impl OpenCADStudio {
|
|||||||
crop.inverted = !crop.inverted;
|
crop.inverted = !crop.inverted;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
"_PCUNCROP" => self.edit_selected_point_clouds(i, "POINTCLOUDUNCROP", |data| data.croppings.clear()),
|
// The Point Cloud Manager docks on the right like External
|
||||||
|
// References and opens expanded.
|
||||||
|
"POINTCLOUDMANAGER" => {
|
||||||
|
let id = crate::ui::dock::PanelId::PointCloudManager;
|
||||||
|
self.pc_manager.show = true;
|
||||||
|
if self.dock.location(id).is_none() {
|
||||||
|
self.dock.dock(id, crate::app::config::DockSide::Right, usize::MAX);
|
||||||
|
}
|
||||||
|
self.dock_expanded = Some(id);
|
||||||
|
}
|
||||||
|
"POINTCLOUDMANAGERCLOSE" => {
|
||||||
|
self.pc_manager.show = false;
|
||||||
|
if self.dock_expanded == Some(crate::ui::dock::PanelId::PointCloudManager) {
|
||||||
|
self.dock_expanded = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"_PCUNCROP" =>self.edit_selected_point_clouds(i, "POINTCLOUDUNCROP", |data| data.croppings.clear()),
|
||||||
"_PDFULUNCLIP" => {
|
"_PDFULUNCLIP" => {
|
||||||
self.edit_selected_underlays(i, "PDFCLIP", |u| {
|
self.edit_selected_underlays(i, "PDFCLIP", |u| {
|
||||||
u.clip_boundary_vertices.clear();
|
u.clip_boundary_vertices.clear();
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ pub(super) const NAMES: &[&str] = &[
|
|||||||
"close_modal",
|
"close_modal",
|
||||||
"pdf_dialog_ok",
|
"pdf_dialog_ok",
|
||||||
"pdf_layer_toggle",
|
"pdf_layer_toggle",
|
||||||
|
"pc_manager_toggle",
|
||||||
"pdf_page_select",
|
"pdf_page_select",
|
||||||
"pc_colormap",
|
"pc_colormap",
|
||||||
"ribbon_tab",
|
"ribbon_tab",
|
||||||
@@ -719,6 +720,19 @@ impl OpenCADStudio {
|
|||||||
_ => return Err(failure("invalid_value", "Unknown color map edit")),
|
_ => return Err(failure("invalid_value", "Unknown color map edit")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// Point Cloud Manager: flip a row's switch, "<handle>:<row>" with
|
||||||
|
// row cloud, unassigned, scans or scan:<name>.
|
||||||
|
"pc_manager_toggle" => {
|
||||||
|
use crate::ui::window::pc_manager::{PcManagerMsg, Row};
|
||||||
|
let value = string(req, "value")?;
|
||||||
|
let parsed = value.split_once(':').and_then(|(handle, row)| {
|
||||||
|
Some((codec::Handle::new(u64::from_str_radix(handle, 16).ok()?), Row::parse(row)?))
|
||||||
|
});
|
||||||
|
let Some((handle, row)) = parsed else {
|
||||||
|
return Err(failure("bad_value", "Expected <handle>:<row>"));
|
||||||
|
};
|
||||||
|
Message::PcManager(PcManagerMsg::Toggle(handle, row))
|
||||||
|
}
|
||||||
// Attach dialog: choose pages by index ("0,2").
|
// Attach dialog: choose pages by index ("0,2").
|
||||||
"pdf_page_select" => {
|
"pdf_page_select" => {
|
||||||
let pages: Vec<usize> = string(req, "value")?
|
let pages: Vec<usize> = string(req, "value")?
|
||||||
|
|||||||
@@ -793,6 +793,8 @@ pub(super) struct OpenCADStudio {
|
|||||||
/// Whether the Browser panel is shown. Off until BROWSER opens it, so
|
/// Whether the Browser panel is shown. Off until BROWSER opens it, so
|
||||||
/// the default layout is unchanged for existing users.
|
/// the default layout is unchanged for existing users.
|
||||||
pub(crate) show_browser: bool,
|
pub(crate) show_browser: bool,
|
||||||
|
/// Point Cloud Manager palette (POINTCLOUDMANAGER).
|
||||||
|
pub(crate) pc_manager: crate::ui::window::pc_manager::PcManager,
|
||||||
/// Which viewport background the colour wheel is editing, or `None` when
|
/// Which viewport background the colour wheel is editing, or `None` when
|
||||||
/// it is closed. One slot, because only one wheel can be open at a time.
|
/// it is closed. One slot, because only one wheel can be open at a time.
|
||||||
pub(crate) bg_picker: Option<BgTarget>,
|
pub(crate) bg_picker: Option<BgTarget>,
|
||||||
@@ -3845,6 +3847,8 @@ pub enum Message {
|
|||||||
PdfImportPick,
|
PdfImportPick,
|
||||||
/// An edit in one of the PDF dialogs.
|
/// An edit in one of the PDF dialogs.
|
||||||
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
||||||
|
/// A click or search in the Point Cloud Manager.
|
||||||
|
PcManager(crate::ui::window::pc_manager::PcManagerMsg),
|
||||||
PdfImportPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
PdfImportPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||||
/// Result of the PDFATTACH file picker.
|
/// Result of the PDFATTACH file picker.
|
||||||
PdfAttachPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
PdfAttachPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||||
@@ -4160,6 +4164,7 @@ impl OpenCADStudio {
|
|||||||
graph_undo_open: false,
|
graph_undo_open: false,
|
||||||
show_external_references: false,
|
show_external_references: false,
|
||||||
show_browser: false,
|
show_browser: false,
|
||||||
|
pc_manager: Default::default(),
|
||||||
bg_picker: None,
|
bg_picker: None,
|
||||||
block_palette: Default::default(),
|
block_palette: Default::default(),
|
||||||
xref_manager: Default::default(),
|
xref_manager: Default::default(),
|
||||||
|
|||||||
@@ -608,6 +608,9 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
PanelId::NodeGraph => {
|
PanelId::NodeGraph => {
|
||||||
self.show_node_graph = false;
|
self.show_node_graph = false;
|
||||||
}
|
}
|
||||||
|
PanelId::PointCloudManager => {
|
||||||
|
self.pc_manager.show = false;
|
||||||
|
}
|
||||||
PanelId::Properties => {
|
PanelId::Properties => {
|
||||||
self.show_properties = false;
|
self.show_properties = false;
|
||||||
self.ribbon.set_properties(false);
|
self.ribbon.set_properties(false);
|
||||||
@@ -712,6 +715,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
PanelId::ExternalReferences => self.show_external_references,
|
PanelId::ExternalReferences => self.show_external_references,
|
||||||
PanelId::Browser => self.show_browser,
|
PanelId::Browser => self.show_browser,
|
||||||
PanelId::NodeGraph => self.show_node_graph,
|
PanelId::NodeGraph => self.show_node_graph,
|
||||||
|
PanelId::PointCloudManager => self.pc_manager.show,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1806,6 +1806,7 @@ impl OpenCADStudio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||||
|
Message::PcManager(message) => self.update_pc_manager(message),
|
||||||
Message::RibbonSelectTab(idx) => {
|
Message::RibbonSelectTab(idx) => {
|
||||||
self.ribbon.select(idx);
|
self.ribbon.select(idx);
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|||||||
@@ -1870,6 +1870,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
|||||||
crate::ui::dock::PanelId::ExternalReferences => self.show_external_references,
|
crate::ui::dock::PanelId::ExternalReferences => self.show_external_references,
|
||||||
crate::ui::dock::PanelId::Browser => self.show_browser,
|
crate::ui::dock::PanelId::Browser => self.show_browser,
|
||||||
crate::ui::dock::PanelId::NodeGraph => self.show_node_graph,
|
crate::ui::dock::PanelId::NodeGraph => self.show_node_graph,
|
||||||
|
crate::ui::dock::PanelId::PointCloudManager => self.pc_manager.show,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let edge_stack = |side: crate::app::config::DockSide| -> Option<Element<'_, Message>> {
|
let edge_stack = |side: crate::app::config::DockSide| -> Option<Element<'_, Message>> {
|
||||||
@@ -2994,6 +2995,13 @@ impl OpenCADStudio {
|
|||||||
auto_collapse,
|
auto_collapse,
|
||||||
),
|
),
|
||||||
crate::ui::dock::PanelId::NodeGraph => tab.graph.panel(width, auto_collapse),
|
crate::ui::dock::PanelId::NodeGraph => tab.graph.panel(width, auto_collapse),
|
||||||
|
crate::ui::dock::PanelId::PointCloudManager => crate::ui::window::pc_manager::view(
|
||||||
|
&self.pc_manager,
|
||||||
|
&tab.scene.document,
|
||||||
|
&tab.scene.selected_handles_in_order(),
|
||||||
|
width,
|
||||||
|
auto_collapse,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let divider = dock_divider(id);
|
let divider = dock_divider(id);
|
||||||
match side {
|
match side {
|
||||||
|
|||||||
@@ -134,6 +134,14 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
|||||||
"A color scheme with this name already exists." => Some(("insert", "pcm-name-taken")),
|
"A color scheme with this name already exists." => Some(("insert", "pcm-name-taken")),
|
||||||
"Invalid range of colorized points." => Some(("insert", "pcm-bad-range")),
|
"Invalid range of colorized points." => Some(("insert", "pcm-bad-range")),
|
||||||
"Sets the color schemes and ranges a point cloud is colored by intensity or elevation." => Some(("insert", "pcm-help")),
|
"Sets the color schemes and ranges a point cloud is colored by intensity or elevation." => Some(("insert", "pcm-help")),
|
||||||
|
"Point Cloud Manager" => Some(("insert", "pc-manager")),
|
||||||
|
"REGIONS AND SCANS" => Some(("insert", "pc-regions-scans")),
|
||||||
|
"Regions" => Some(("insert", "pc-regions")),
|
||||||
|
"Unassigned Points" => Some(("insert", "pc-unassigned")),
|
||||||
|
"Scans" => Some(("insert", "pc-scans")),
|
||||||
|
"Search" => Some(("insert", "pc-search")),
|
||||||
|
"Collapse all" => Some(("insert", "pc-collapse-all")),
|
||||||
|
"Expand all" => Some(("insert", "pc-expand-all")),
|
||||||
"Stylization" => Some(("properties", "stylization")),
|
"Stylization" => Some(("properties", "stylization")),
|
||||||
"Color scheme" => Some(("properties", "color-scheme")),
|
"Color scheme" => Some(("properties", "color-scheme")),
|
||||||
"Scan Colors" => Some(("properties", "scan-colors")),
|
"Scan Colors" => Some(("properties", "scan-colors")),
|
||||||
|
|||||||
+7
-1
@@ -8403,7 +8403,13 @@ impl Scene {
|
|||||||
common.color.clone()
|
common.color.clone()
|
||||||
};
|
};
|
||||||
let object_color = color.rgb().map_or([255; 3], |(r, g, b)| [r, g, b]);
|
let object_color = color.rgb().map_or([255; 3], |(r, g, b)| [r, g, b]);
|
||||||
clouds.extend(point_cloud::placed(&self.document, data, object_color, transform));
|
clouds.extend(point_cloud::placed(
|
||||||
|
&self.document,
|
||||||
|
data,
|
||||||
|
object_color,
|
||||||
|
transform,
|
||||||
|
&point_cloud::hidden(common),
|
||||||
|
));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if clouds.is_empty() {
|
if clouds.is_empty() {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub struct CloudPoints {
|
|||||||
pub has_normals: bool,
|
pub has_normals: bool,
|
||||||
/// Scans read (one for a scan file).
|
/// Scans read (one for a scan file).
|
||||||
pub scans: usize,
|
pub scans: usize,
|
||||||
|
/// Each scan's name (its file stem) and its points' index range.
|
||||||
|
pub scan_ranges: Vec<(String, std::ops::Range<usize>)>,
|
||||||
/// A project's preview picture (JPEG).
|
/// A project's preview picture (JPEG).
|
||||||
pub preview: Option<Vec<u8>>,
|
pub preview: Option<Vec<u8>>,
|
||||||
/// Bounds from the scans' headers, placed like their points: the
|
/// Bounds from the scans' headers, placed like their points: the
|
||||||
@@ -70,18 +72,19 @@ fn decode(path: &Path) -> Option<CloudPoints> {
|
|||||||
.is_some_and(|e| e.eq_ignore_ascii_case("rcp"));
|
.is_some_and(|e| e.eq_ignore_ascii_case("rcp"));
|
||||||
// A project lists its scans with their own transforms; a scan file
|
// A project lists its scans with their own transforms; a scan file
|
||||||
// carries the same transform in its header.
|
// carries the same transform in its header.
|
||||||
let scans: Vec<(rcs::Scan, [[f64; 3]; 3])> = if is_project {
|
let stem = |p: &Path| p.file_stem().map_or_else(String::new, |s| s.to_string_lossy().into_owned());
|
||||||
|
let scans: Vec<(rcs::Scan, [[f64; 3]; 3], String)> = if is_project {
|
||||||
rcp::scans(&bytes, path)?
|
rcp::scans(&bytes, path)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|entry| {
|
.filter_map(|entry| {
|
||||||
let scan = rcs::decode(&std::fs::read(&entry.path).ok()?)?;
|
let scan = rcs::decode(&std::fs::read(&entry.path).ok()?)?;
|
||||||
Some((scan, [entry.translation, entry.rotation, entry.scale]))
|
Some((scan, [entry.translation, entry.rotation, entry.scale], stem(&entry.path)))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
let scan = rcs::decode(&bytes)?;
|
let scan = rcs::decode(&bytes)?;
|
||||||
let transform = [scan.translation, scan.rotation, scan.scale];
|
let transform = [scan.translation, scan.rotation, scan.scale];
|
||||||
vec![(scan, transform)]
|
vec![(scan, transform, stem(path))]
|
||||||
};
|
};
|
||||||
let mut cloud = CloudPoints {
|
let mut cloud = CloudPoints {
|
||||||
id: NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
|
id: NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
|
||||||
@@ -89,21 +92,24 @@ fn decode(path: &Path) -> Option<CloudPoints> {
|
|||||||
colors: Vec::new(),
|
colors: Vec::new(),
|
||||||
intensity: Vec::new(),
|
intensity: Vec::new(),
|
||||||
normals: Vec::new(),
|
normals: Vec::new(),
|
||||||
has_rgb: scans.iter().any(|(scan, _)| scan.has_rgb),
|
has_rgb: scans.iter().any(|(scan, ..)| scan.has_rgb),
|
||||||
has_intensity: scans.iter().any(|(scan, _)| scan.has_intensity),
|
has_intensity: scans.iter().any(|(scan, ..)| scan.has_intensity),
|
||||||
has_normals: scans.iter().any(|(scan, _)| scan.has_normals),
|
has_normals: scans.iter().any(|(scan, ..)| scan.has_normals),
|
||||||
scans: scans.len(),
|
scans: scans.len(),
|
||||||
|
scan_ranges: Vec::new(),
|
||||||
preview: is_project.then(|| rcp::preview(&bytes)).flatten(),
|
preview: is_project.then(|| rcp::preview(&bytes)).flatten(),
|
||||||
bounds: None,
|
bounds: None,
|
||||||
};
|
};
|
||||||
for (scan, [translation, rotation, scale]) in scans {
|
for (scan, [translation, rotation, scale], name) in scans {
|
||||||
let rotate = euler(rotation.map(f64::to_radians));
|
let rotate = euler(rotation.map(f64::to_radians));
|
||||||
let place = |p: &[f64; 3]| {
|
let place = |p: &[f64; 3]| {
|
||||||
let s = [p[0] * scale[0], p[1] * scale[1], p[2] * scale[2]];
|
let s = [p[0] * scale[0], p[1] * scale[1], p[2] * scale[2]];
|
||||||
let r = rotate.map(|row| row[0] * s[0] + row[1] * s[1] + row[2] * s[2]);
|
let r = rotate.map(|row| row[0] * s[0] + row[1] * s[1] + row[2] * s[2]);
|
||||||
[r[0] + translation[0], r[1] + translation[1], r[2] + translation[2]]
|
[r[0] + translation[0], r[1] + translation[1], r[2] + translation[2]]
|
||||||
};
|
};
|
||||||
|
let first = cloud.positions.len();
|
||||||
cloud.positions.extend(scan.local.iter().map(place));
|
cloud.positions.extend(scan.local.iter().map(place));
|
||||||
|
cloud.scan_ranges.push((name, first..cloud.positions.len()));
|
||||||
let [lo, hi] = scan.bounds;
|
let [lo, hi] = scan.bounds;
|
||||||
for corner in 0..8 {
|
for corner in 0..8 {
|
||||||
let p = place(&[
|
let p = place(&[
|
||||||
@@ -249,16 +255,46 @@ pub(crate) fn any_definition(document: &CadDocument) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What the Point Cloud Manager turned off on a cloud: extended data under
|
||||||
|
/// this application, one string per hidden scan plus the markers below.
|
||||||
|
pub const VISIBILITY_APP: &str = "OCS_POINTCLOUD_VISIBILITY";
|
||||||
|
/// The whole cloud is off.
|
||||||
|
pub const CLOUD_OFF: &str = "*CLOUD_OFF";
|
||||||
|
/// Its unassigned points (every point of a cloud without regions) are off.
|
||||||
|
pub const UNASSIGNED_OFF: &str = "*UNASSIGNED_OFF";
|
||||||
|
|
||||||
|
/// The scans and markers a cloud entity hides.
|
||||||
|
pub(crate) fn hidden(common: &codec::entities::EntityCommon) -> Vec<String> {
|
||||||
|
common
|
||||||
|
.extended_data
|
||||||
|
.records()
|
||||||
|
.iter()
|
||||||
|
.filter(|r| r.application_name.eq_ignore_ascii_case(VISIBILITY_APP))
|
||||||
|
.flat_map(|r| r.values.iter())
|
||||||
|
.filter_map(|v| match v {
|
||||||
|
codec::xdata::XDataValue::String(s) => Some(s.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// The points `data` shows, placed through its origin and axes and then
|
/// The points `data` shows, placed through its origin and axes and then
|
||||||
/// `transform` (a block instance), with its crops applied. `None` when the
|
/// `transform` (a block instance), with its crops applied and the `hidden`
|
||||||
/// scan file is missing or unreadable.
|
/// scans left out. `None` when the scan file is missing or unreadable.
|
||||||
pub(crate) fn placed(
|
pub(crate) fn placed(
|
||||||
document: &CadDocument,
|
document: &CadDocument,
|
||||||
data: &PointCloudExData,
|
data: &PointCloudExData,
|
||||||
object_color: [u8; 3],
|
object_color: [u8; 3],
|
||||||
transform: Option<&Transform>,
|
transform: Option<&Transform>,
|
||||||
|
hidden: &[String],
|
||||||
) -> Option<Arc<PlacedCloud>> {
|
) -> Option<Arc<PlacedCloud>> {
|
||||||
let cloud = load(&resolve_source(document, data)?)?;
|
let cloud = load(&resolve_source(document, data)?)?;
|
||||||
|
let skipped: Vec<std::ops::Range<usize>> =
|
||||||
|
if hidden.iter().any(|h| h == CLOUD_OFF || h == UNASSIGNED_OFF) {
|
||||||
|
vec![0..cloud.positions.len()]
|
||||||
|
} else {
|
||||||
|
cloud.scan_ranges.iter().filter(|(name, _)| hidden.contains(name)).map(|(_, r)| r.clone()).collect()
|
||||||
|
};
|
||||||
// The points shown: POINTCLOUDDENSITY per cent of POINTCLOUDPOINTMAX,
|
// The points shown: POINTCLOUDDENSITY per cent of POINTCLOUDPOINTMAX,
|
||||||
// in tenths by POINTCLOUDLOD, every n-th point of a larger cloud.
|
// in tenths by POINTCLOUDLOD, every n-th point of a larger cloud.
|
||||||
let shown = |name| setting(name).map_or(0, |s| setting_value(document, s));
|
let shown = |name| setting(name).map_or(0, |s| setting_value(document, s));
|
||||||
@@ -298,6 +334,7 @@ pub(crate) fn placed(
|
|||||||
}
|
}
|
||||||
step.hash(&mut h);
|
step.hash(&mut h);
|
||||||
style.hash(&mut h);
|
style.hash(&mut h);
|
||||||
|
skipped.hash(&mut h);
|
||||||
let key = h.finish();
|
let key = h.finish();
|
||||||
|
|
||||||
type Placed = Mutex<FxHashMap<u64, Weak<PlacedCloud>>>;
|
type Placed = Mutex<FxHashMap<u64, Weak<PlacedCloud>>>;
|
||||||
@@ -369,7 +406,7 @@ pub(crate) fn placed(
|
|||||||
};
|
};
|
||||||
let mut instances = Vec::with_capacity(cloud.positions.len() / step + 1);
|
let mut instances = Vec::with_capacity(cloud.positions.len() / step + 1);
|
||||||
for (i, p) in cloud.positions.iter().enumerate().step_by(step) {
|
for (i, p) in cloud.positions.iter().enumerate().step_by(step) {
|
||||||
if !crops.iter().all(|crop| keeps(crop, p)) {
|
if skipped.iter().any(|r| r.contains(&i)) || !crops.iter().all(|crop| keeps(crop, p)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let w = world(p);
|
let w = world(p);
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ pub enum PanelId {
|
|||||||
Browser,
|
Browser,
|
||||||
/// Node library of the node graph overlay.
|
/// Node library of the node graph overlay.
|
||||||
NodeGraph,
|
NodeGraph,
|
||||||
|
/// Regions and scans of the drawing's point clouds.
|
||||||
|
PointCloudManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PanelId {
|
impl PanelId {
|
||||||
@@ -57,6 +59,7 @@ impl PanelId {
|
|||||||
PanelId::ExternalReferences => "External References",
|
PanelId::ExternalReferences => "External References",
|
||||||
PanelId::Browser => "Browser",
|
PanelId::Browser => "Browser",
|
||||||
PanelId::NodeGraph => "Node Graph",
|
PanelId::NodeGraph => "Node Graph",
|
||||||
|
PanelId::PointCloudManager => "Point Cloud Manager",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +71,7 @@ impl PanelId {
|
|||||||
PanelId::ExternalReferences => 460.0,
|
PanelId::ExternalReferences => 460.0,
|
||||||
PanelId::Browser => 230.0,
|
PanelId::Browser => 230.0,
|
||||||
PanelId::NodeGraph => 220.0,
|
PanelId::NodeGraph => 220.0,
|
||||||
|
PanelId::PointCloudManager => 280.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +161,7 @@ impl DockState {
|
|||||||
PanelId::ExternalReferences,
|
PanelId::ExternalReferences,
|
||||||
PanelId::Browser,
|
PanelId::Browser,
|
||||||
PanelId::NodeGraph,
|
PanelId::NodeGraph,
|
||||||
|
PanelId::PointCloudManager,
|
||||||
] {
|
] {
|
||||||
self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub mod annotation_data;
|
|||||||
pub mod alias_editor;
|
pub mod alias_editor;
|
||||||
pub mod find_replace;
|
pub mod find_replace;
|
||||||
pub mod named_parameters;
|
pub mod named_parameters;
|
||||||
|
pub mod pc_manager;
|
||||||
pub mod pdf_dialogs;
|
pub mod pdf_dialogs;
|
||||||
pub mod xref_attach;
|
pub mod xref_attach;
|
||||||
pub mod xref_help;
|
pub mod xref_help;
|
||||||
|
|||||||
@@ -0,0 +1,326 @@
|
|||||||
|
//! Point Cloud Manager (POINTCLOUDMANAGER): the regions and scans of the
|
||||||
|
//! drawing's point clouds as a tree, each with a switch that shows or hides
|
||||||
|
//! its points. The switches are the cloud's own visibility extended data, so
|
||||||
|
//! the tree is read straight from the document on every view.
|
||||||
|
|
||||||
|
use crate::app::Message;
|
||||||
|
use crate::scene::model::point_cloud::{self, CLOUD_OFF, UNASSIGNED_OFF};
|
||||||
|
use crate::ui::dock::PanelId;
|
||||||
|
use codec::entities::ExtendedEntityData;
|
||||||
|
use codec::{CadDocument, EntityType, Handle};
|
||||||
|
use iced::widget::{button, checkbox, column, container, mouse_area, row, scrollable, text, text_input, tooltip, Space};
|
||||||
|
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
const CLOUD_ICON: &[u8] = include_bytes!("../../../assets/icons/pc_attach.svg");
|
||||||
|
const REGION_ICON: &[u8] = include_bytes!("../../../assets/icons/region.svg");
|
||||||
|
const UNASSIGNED_ICON: &[u8] = include_bytes!("../../../assets/icons/revcloud.svg");
|
||||||
|
|
||||||
|
/// Palette state that is not in the drawing: shown, folded nodes, the
|
||||||
|
/// highlighted row and the search text.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PcManager {
|
||||||
|
pub show: bool,
|
||||||
|
pub collapsed: HashSet<String>,
|
||||||
|
pub selected: Option<String>,
|
||||||
|
pub search: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A row with a switch.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Row {
|
||||||
|
Cloud,
|
||||||
|
Unassigned,
|
||||||
|
Scans,
|
||||||
|
Scan(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Row {
|
||||||
|
/// `cloud`, `unassigned`, `scans` or `scan:<name>` (automation).
|
||||||
|
pub fn parse(text: &str) -> Option<Self> {
|
||||||
|
match text {
|
||||||
|
"cloud" => Some(Self::Cloud),
|
||||||
|
"unassigned" => Some(Self::Unassigned),
|
||||||
|
"scans" => Some(Self::Scans),
|
||||||
|
_ => text.strip_prefix("scan:").map(|name| Self::Scan(name.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PcManagerMsg {
|
||||||
|
Toggle(Handle, Row),
|
||||||
|
Expand(String),
|
||||||
|
Select(String),
|
||||||
|
Search(String),
|
||||||
|
CollapseAll,
|
||||||
|
ExpandAll,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One cloud of the tree.
|
||||||
|
pub struct Cloud {
|
||||||
|
pub handle: Handle,
|
||||||
|
pub name: String,
|
||||||
|
pub hidden: Vec<String>,
|
||||||
|
pub scans: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The drawing's point clouds; only the selected ones when any is selected.
|
||||||
|
pub fn clouds(document: &CadDocument, selected: &[Handle]) -> Vec<Cloud> {
|
||||||
|
let all: Vec<Cloud> = document
|
||||||
|
.entities()
|
||||||
|
.filter_map(|entity| {
|
||||||
|
let EntityType::Extended(extended) = entity else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let ExtendedEntityData::PointCloudEx(data) = &extended.data else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let common = entity.common();
|
||||||
|
let scans = point_cloud::resolve_source(document, data)
|
||||||
|
.and_then(|path| point_cloud::load(&path))
|
||||||
|
.map(|cloud| cloud.scan_ranges.iter().map(|(name, _)| name.clone()).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
Some(Cloud {
|
||||||
|
handle: common.handle,
|
||||||
|
name: if data.name.is_empty() { format!("{:X}", common.handle.value()) } else { data.name.clone() },
|
||||||
|
hidden: point_cloud::hidden(common),
|
||||||
|
scans,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if all.iter().any(|cloud| selected.contains(&cloud.handle)) {
|
||||||
|
all.into_iter().filter(|cloud| selected.contains(&cloud.handle)).collect()
|
||||||
|
} else {
|
||||||
|
all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a row's switch is on. A parent is on while any child is.
|
||||||
|
pub fn is_on(hidden: &[String], scans: &[String], row: &Row) -> bool {
|
||||||
|
let off = |name: &str| hidden.iter().any(|h| h == name);
|
||||||
|
let any_scan = scans.is_empty() || scans.iter().any(|s| !off(s));
|
||||||
|
match row {
|
||||||
|
Row::Scan(name) => !off(name),
|
||||||
|
Row::Scans => any_scan,
|
||||||
|
Row::Unassigned => !off(UNASSIGNED_OFF),
|
||||||
|
Row::Cloud => !off(CLOUD_OFF) && (!off(UNASSIGNED_OFF) || any_scan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The hidden list after flipping `row`. Switching the cloud back on shows
|
||||||
|
/// everything under it.
|
||||||
|
pub fn toggled(hidden: &[String], scans: &[String], row: &Row) -> Vec<String> {
|
||||||
|
fn set(out: &mut Vec<String>, name: &str, off: bool) {
|
||||||
|
out.retain(|h| h != name);
|
||||||
|
if off {
|
||||||
|
out.push(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let on = is_on(hidden, scans, row);
|
||||||
|
let mut out = hidden.to_vec();
|
||||||
|
match row {
|
||||||
|
Row::Cloud if on => set(&mut out, CLOUD_OFF, true),
|
||||||
|
Row::Cloud => out.clear(),
|
||||||
|
Row::Unassigned => set(&mut out, UNASSIGNED_OFF, on),
|
||||||
|
Row::Scans => scans.iter().for_each(|s| set(&mut out, s, on)),
|
||||||
|
Row::Scan(name) => set(&mut out, name, on),
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keys of the rows that fold: each cloud and its Scans.
|
||||||
|
pub fn folding_keys(clouds: &[Cloud]) -> Vec<String> {
|
||||||
|
clouds.iter().flat_map(|c| [key(c.handle), format!("{}/scans", key(c.handle))]).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key(handle: Handle) -> String {
|
||||||
|
format!("{:X}", handle.value())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg(m: PcManagerMsg) -> Message {
|
||||||
|
Message::PcManager(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One tree row: indent, fold arrow, icon, label and switch (`None` for a
|
||||||
|
/// row that has none to give, drawn off and disabled).
|
||||||
|
fn tree_row<'a>(
|
||||||
|
state: &PcManager,
|
||||||
|
key: String,
|
||||||
|
depth: u16,
|
||||||
|
fold: Option<bool>,
|
||||||
|
icon: Element<'a, Message>,
|
||||||
|
label: String,
|
||||||
|
switch: Option<(bool, Message)>,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let arrow: Element<'a, Message> = match fold {
|
||||||
|
Some(open) => button(if open {
|
||||||
|
crate::ui::icons::themed_arrow_down(10.0)
|
||||||
|
} else {
|
||||||
|
crate::ui::icons::themed_arrow_right(10.0)
|
||||||
|
})
|
||||||
|
.on_press(msg(PcManagerMsg::Expand(key.clone())))
|
||||||
|
.style(button::text)
|
||||||
|
.padding(2)
|
||||||
|
.into(),
|
||||||
|
None => Space::new().width(14).into(),
|
||||||
|
};
|
||||||
|
let enabled = switch.is_some();
|
||||||
|
let label = text(label).size(12).width(Fill);
|
||||||
|
let label = if enabled { label } else { label.style(crate::ui::style::common::muted_style) };
|
||||||
|
let switch: Element<'a, Message> = match switch {
|
||||||
|
Some((on, message)) => checkbox(on).on_toggle(move |_| message.clone()).size(14).into(),
|
||||||
|
None => checkbox(false).size(14).into(),
|
||||||
|
};
|
||||||
|
let selected = state.selected.as_deref() == Some(key.as_str());
|
||||||
|
let cells = row![Space::new().width(Length::Fixed(f32::from(depth) * 16.0)), arrow, icon, label, switch]
|
||||||
|
.spacing(5)
|
||||||
|
.align_y(iced::Center);
|
||||||
|
mouse_area(
|
||||||
|
container(cells)
|
||||||
|
.width(Fill)
|
||||||
|
.padding([3, 6])
|
||||||
|
.style(move |theme: &Theme| container::Style {
|
||||||
|
background: selected.then(|| Background::Color(theme.palette().primary.weak.color)),
|
||||||
|
text_color: selected.then(|| theme.palette().primary.weak.text),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_press(msg(PcManagerMsg::Select(key)))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_button<'a>(icon: &'static [u8], tip: String, message: PcManagerMsg) -> Element<'a, Message> {
|
||||||
|
let b = button(crate::ui::icons::themed_secondary(icon, 12.0))
|
||||||
|
.on_press(msg(message))
|
||||||
|
.style(button::subtle)
|
||||||
|
.padding([2, 4]);
|
||||||
|
tooltip(b, text(tip).size(10), tooltip::Position::Bottom).gap(4).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view<'a>(
|
||||||
|
state: &'a PcManager,
|
||||||
|
document: &'a CadDocument,
|
||||||
|
selected: &[Handle],
|
||||||
|
width: f32,
|
||||||
|
auto_collapse: bool,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let title_bar =
|
||||||
|
crate::ui::dock::title_bar(PanelId::PointCloudManager, crate::t!("Point Cloud Manager").into_owned(), auto_collapse);
|
||||||
|
let query = state.search.trim().to_lowercase();
|
||||||
|
let matches = |label: &str| query.is_empty() || label.to_lowercase().contains(&query);
|
||||||
|
// Searching opens every node so the matches show.
|
||||||
|
let open = |key: &str| !query.is_empty() || !state.collapsed.contains(key);
|
||||||
|
let regions = crate::t!("Regions").into_owned();
|
||||||
|
let unassigned = crate::t!("Unassigned Points").into_owned();
|
||||||
|
let scans_label = crate::t!("Scans").into_owned();
|
||||||
|
|
||||||
|
let mut tree = column![].spacing(1);
|
||||||
|
for cloud in clouds(document, selected) {
|
||||||
|
let root = key(cloud.handle);
|
||||||
|
let scans: Vec<&String> = cloud.scans.iter().filter(|s| matches(s)).collect();
|
||||||
|
let show_scans = matches(&scans_label) || !scans.is_empty();
|
||||||
|
let children = matches(®ions) || matches(&unassigned) || show_scans;
|
||||||
|
if !matches(&cloud.name) && !children {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let switch = |row: Row| {
|
||||||
|
let on = is_on(&cloud.hidden, &cloud.scans, &row);
|
||||||
|
Some((on, msg(PcManagerMsg::Toggle(cloud.handle, row))))
|
||||||
|
};
|
||||||
|
let cloud_icon = || crate::ui::icons::semantic(CLOUD_ICON, 14.0);
|
||||||
|
tree = tree.push(tree_row(
|
||||||
|
state,
|
||||||
|
root.clone(),
|
||||||
|
0,
|
||||||
|
Some(open(&root)),
|
||||||
|
cloud_icon(),
|
||||||
|
cloud.name.clone(),
|
||||||
|
switch(Row::Cloud),
|
||||||
|
));
|
||||||
|
if !open(&root) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if matches(®ions) {
|
||||||
|
// Region data is not read: the row is there, off and disabled.
|
||||||
|
tree = tree.push(tree_row(
|
||||||
|
state,
|
||||||
|
format!("{root}/regions"),
|
||||||
|
1,
|
||||||
|
None,
|
||||||
|
crate::ui::icons::semantic_disabled(REGION_ICON, 14.0),
|
||||||
|
regions.clone(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if matches(&unassigned) {
|
||||||
|
tree = tree.push(tree_row(
|
||||||
|
state,
|
||||||
|
format!("{root}/unassigned"),
|
||||||
|
1,
|
||||||
|
None,
|
||||||
|
crate::ui::icons::semantic(UNASSIGNED_ICON, 14.0),
|
||||||
|
unassigned.clone(),
|
||||||
|
switch(Row::Unassigned),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if show_scans {
|
||||||
|
let scans_key = format!("{root}/scans");
|
||||||
|
let scans_open = open(&scans_key);
|
||||||
|
tree = tree.push(tree_row(
|
||||||
|
state,
|
||||||
|
scans_key.clone(),
|
||||||
|
1,
|
||||||
|
Some(scans_open),
|
||||||
|
cloud_icon(),
|
||||||
|
scans_label.clone(),
|
||||||
|
switch(Row::Scans),
|
||||||
|
));
|
||||||
|
if scans_open {
|
||||||
|
for name in scans {
|
||||||
|
tree = tree.push(tree_row(
|
||||||
|
state,
|
||||||
|
format!("{scans_key}/{name}"),
|
||||||
|
2,
|
||||||
|
None,
|
||||||
|
cloud_icon(),
|
||||||
|
name.clone(),
|
||||||
|
switch(Row::Scan(name.clone())),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let heading = text(crate::t!("REGIONS AND SCANS").into_owned())
|
||||||
|
.size(11)
|
||||||
|
.font(iced::Font {
|
||||||
|
weight: iced::font::Weight::Bold,
|
||||||
|
style: iced::font::Style::Italic,
|
||||||
|
..iced::Font::DEFAULT
|
||||||
|
})
|
||||||
|
.style(|theme: &Theme| text::Style { color: Some(theme.palette().primary.base.color) })
|
||||||
|
.width(Fill);
|
||||||
|
let header = row![
|
||||||
|
heading,
|
||||||
|
fold_button(crate::ui::icons::MINUS, crate::t!("Collapse all").into_owned(), PcManagerMsg::CollapseAll),
|
||||||
|
fold_button(crate::ui::icons::PLUS, crate::t!("Expand all").into_owned(), PcManagerMsg::ExpandAll),
|
||||||
|
]
|
||||||
|
.spacing(2)
|
||||||
|
.align_y(iced::Center);
|
||||||
|
let card = container(column![header, scrollable(tree).height(Fill)].spacing(6))
|
||||||
|
.padding(6)
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.style(|theme: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(theme.palette().background.weak.color)),
|
||||||
|
border: Border { radius: 4.0.into(), ..Default::default() },
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let search = text_input(&crate::t!("Search"), &state.search)
|
||||||
|
.on_input(|v| msg(PcManagerMsg::Search(v)))
|
||||||
|
.padding([4, 8])
|
||||||
|
.size(12);
|
||||||
|
|
||||||
|
crate::ui::dock::frame(column![title_bar, card, search].spacing(6), width)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user