feat(ui): add viewport tooltips

Reuse ribbon tooltip styling for every viewport toolbar action and expose translated action names with related commands.

Refs #482
This commit is contained in:
Hakan Seven committed 2026-08-04 19:10:47 +03:00
1 parent 7c893d8f5f
commit 44d0c65ae9
10 files changed
+115 -17

No files matched your search

+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = Die Zwischenablage enthält keinen unterstützt
clipboard-unavailable = Die Systemzwischenablage ist nicht verfügbar.
clipboard-occupied = Die Systemzwischenablage wird gerade verwendet. Versuchen Sie es erneut.
clipboard-conversion-failed = Der Text in der Zwischenablage konnte nicht gelesen werden.
viewport-split-vertical = Ansichtsfenster vertikal teilen
viewport-split-horizontal = Ansichtsfenster horizontal teilen
viewport-move = Ansichtsfenster verschieben
viewport-close = Ansichtsfenster schließen
modal-point-style = Punktstil
modal-attribute-editor = Attribut-Editor
modal-save-drawing-as = Zeichnung speichern als
+4
View File
@@ -113,6 +113,10 @@ clipboard-no-supported-content = Clipboard does not contain supported text or dr
clipboard-unavailable = System clipboard is unavailable.
clipboard-occupied = System clipboard is currently in use. Try again.
clipboard-conversion-failed = Clipboard text could not be read.
viewport-split-vertical = Split Viewport Vertically
viewport-split-horizontal = Split Viewport Horizontally
viewport-move = Move Viewport
viewport-close = Close Viewport
modal-point-style = Point Style
modal-attribute-editor = Attribute Editor
modal-save-drawing-as = Save Drawing As
+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = Le presse-papiers ne contient aucun texte ni ob
clipboard-unavailable = Le presse-papiers système n’est pas disponible.
clipboard-occupied = Le presse-papiers système est en cours d’utilisation. Réessayez.
clipboard-conversion-failed = Impossible de lire le texte du presse-papiers.
viewport-split-vertical = Diviser la fenêtre verticalement
viewport-split-horizontal = Diviser la fenêtre horizontalement
viewport-move = Déplacer la fenêtre
viewport-close = Fermer la fenêtre
modal-point-style = Style de point
modal-attribute-editor = Éditeur d'attributs
modal-save-drawing-as = Enregistrer le dessin sous
+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = क्लिपबोर्ड में सम
clipboard-unavailable = सिस्टम क्लिपबोर्ड उपलब्ध नहीं है।
clipboard-occupied = सिस्टम क्लिपबोर्ड अभी उपयोग में है। फिर प्रयास करें।
clipboard-conversion-failed = क्लिपबोर्ड का टेक्स्ट पढ़ा नहीं जा सका।
viewport-split-vertical = व्यूपोर्ट को लंबवत विभाजित करें
viewport-split-horizontal = व्यूपोर्ट को क्षैतिज विभाजित करें
viewport-move = व्यूपोर्ट ले जाएँ
viewport-close = व्यूपोर्ट बंद करें
modal-point-style = प्वाइंट स्टाइल
modal-attribute-editor = विशेषता संपादक
modal-save-drawing-as = ड्रॉइंग इस रूप में सहेजें
+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = Het klembord bevat geen ondersteunde tekst of t
clipboard-unavailable = Het systeemklembord is niet beschikbaar.
clipboard-occupied = Het systeemklembord is momenteel in gebruik. Probeer het opnieuw.
clipboard-conversion-failed = De tekst op het klembord kon niet worden gelezen.
viewport-split-vertical = Viewport verticaal splitsen
viewport-split-horizontal = Viewport horizontaal splitsen
viewport-move = Viewport verplaatsen
viewport-close = Viewport sluiten
modal-point-style = Puntstijl
modal-attribute-editor = Kenmerkeditor
modal-save-drawing-as = Tekening opslaan als
+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = Буфер обмена не содержит
clipboard-unavailable = Системный буфер обмена недоступен.
clipboard-occupied = Системный буфер обмена сейчас используется. Повторите попытку.
clipboard-conversion-failed = Не удалось прочитать текст из буфера обмена.
viewport-split-vertical = Разделить видовой экран по вертикали
viewport-split-horizontal = Разделить видовой экран по горизонтали
viewport-move = Переместить видовой экран
viewport-close = Закрыть видовой экран
modal-point-style = Стиль точек
modal-attribute-editor = Редактор атрибутов
modal-save-drawing-as = Сохранить чертеж как
+4
View File
@@ -113,6 +113,10 @@ clipboard-no-supported-content = Panoda desteklenen metin veya çizim nesnesi yo
clipboard-unavailable = Sistem panosuna erişilemiyor.
clipboard-occupied = Sistem panosu şu anda kullanılıyor. Yeniden deneyin.
clipboard-conversion-failed = Panodaki metin okunamadı.
viewport-split-vertical = Görünüm Penceresini Dikey Böl
viewport-split-horizontal = Görünüm Penceresini Yatay Böl
viewport-move = Görünüm Penceresini Taşı
viewport-close = Görünüm Penceresini Kapat
modal-point-style = Nokta Stili
modal-attribute-editor = Öznitelik Düzenleyicisi
modal-save-drawing-as = Çizimi Farklı Kaydet
+4
View File
@@ -94,6 +94,10 @@ clipboard-no-supported-content = 剪贴板中没有受支持的文本或图形
clipboard-unavailable = 系统剪贴板不可用。
clipboard-occupied = 系统剪贴板当前正被使用,请重试。
clipboard-conversion-failed = 无法读取剪贴板文本。
viewport-split-vertical = 垂直拆分视口
viewport-split-horizontal = 水平拆分视口
viewport-move = 移动视口
viewport-close = 关闭视口
modal-point-style = 点样式
modal-attribute-editor = 属性编辑器
modal-save-drawing-as = 图纸另存为
+75 -17
View File
@@ -1,10 +1,26 @@
use super::*;
use super::super::document::{DynComponent, DynFieldEntry};
use super::super::Message;
use iced::widget::{
button, container, mouse_area, row,
};
use iced::widget::{button, container, mouse_area, row, tooltip};
use iced::{Background, Border, Element, Theme};
use std::time::Duration;
fn viewport_tooltip<'a>(
control: impl Into<Element<'a, Message>>,
title: String,
command: &'static str,
) -> Element<'a, Message> {
let text = format!("{title}\n{} {command}", crate::t!("Command:"));
tooltip(
control,
crate::ui::ribbon::tooltip_content(text),
tooltip::Position::Bottom,
)
.gap(6.0)
.delay(Duration::from_millis(400))
.style(crate::ui::ribbon::tooltip_style)
.into()
}
pub(super) fn viewport_controls<'a>(
render_mode: acadrust::entities::ViewportRenderMode,
@@ -23,8 +39,11 @@ pub(super) fn viewport_controls<'a>(
RenderModeChoice(M::FlatShadedWithEdges),
RenderModeChoice(M::GouraudShadedWithEdges),
];
let danger_btn = move |bytes: &'static [u8], msg: Message| {
button(crate::ui::icons::themed_danger(bytes, 15.0))
let danger_btn = move |bytes: &'static [u8],
msg: Message,
title: String,
command: &'static str| {
let button = button(crate::ui::icons::themed_danger(bytes, 15.0))
.on_press(msg)
.padding([4, 6])
.style(move |theme: &Theme, status| iced::widget::button::Style {
@@ -33,26 +52,29 @@ pub(super) fn viewport_controls<'a>(
iced::widget::button::Status::Hovered
| iced::widget::button::Status::Pressed
)
.then_some(Background::Color(
theme.palette().danger.weak.color
)),
.then_some(Background::Color(theme.palette().danger.weak.color)),
border: Border {
radius: 3.0.into(),
..Default::default()
},
text_color: theme.palette().danger.base.color,
..Default::default()
})
});
viewport_tooltip(button, title, command)
};
// Borderless icon button; an `active` toggle gets an accent tint + fill.
let icon_btn = move |bytes: &'static [u8], active: bool, msg: Message| {
let icon_btn = move |bytes: &'static [u8],
active: bool,
msg: Message,
title: String,
command: &'static str| {
let icon = if active {
crate::ui::icons::themed_primary(bytes, 15.0)
} else {
crate::ui::icons::themed(bytes, 15.0)
};
button(icon)
let button = button(icon)
.on_press(msg)
.padding([4, 6])
.style(move |theme: &Theme, status| {
@@ -75,7 +97,8 @@ pub(super) fn viewport_controls<'a>(
.unwrap_or(palette.background.base.text),
..Default::default()
}
})
});
viewport_tooltip(button, title, command)
};
// Render-mode picker, restyled borderless so the outer chip frames it.
@@ -100,6 +123,11 @@ pub(super) fn viewport_controls<'a>(
handle_color: text,
}
});
let picker = viewport_tooltip(
picker,
crate::t!("Visual Style").into_owned(),
"VISUALSTYLES",
);
// Thin vertical divider between control groups.
let sep = || {
@@ -117,17 +145,41 @@ pub(super) fn viewport_controls<'a>(
.spacing(3)
.align_y(iced::alignment::Vertical::Center);
bar = bar
.push(icon_btn(crate::ui::icons::GRID, show_grid, Message::ToggleGrid))
.push(icon_btn(
crate::ui::icons::GRID,
show_grid,
Message::ToggleGrid,
crate::t!("Toggle Grid").into_owned(),
"GRID",
))
.push(sep())
.push(icon_btn(crate::ui::icons::SNAP, snap_on, Message::ToggleGridSnap))
.push(icon_btn(
crate::ui::icons::SNAP,
snap_on,
Message::ToggleGridSnap,
crate::t!("Toggle Grid Snap").into_owned(),
"SNAP",
))
.push(sep())
.push(picker);
if include_split {
bar = bar
.push(sep())
.push(icon_btn(crate::ui::icons::SPLIT_V, false, Message::SplitModelViewport(false)))
.push(icon_btn(
crate::ui::icons::SPLIT_V,
false,
Message::SplitModelViewport(false),
crate::tr!("viewport-split-vertical"),
"VPORTS 2V",
))
.push(sep())
.push(icon_btn(crate::ui::icons::SPLIT_H, false, Message::SplitModelViewport(true)));
.push(icon_btn(
crate::ui::icons::SPLIT_H,
false,
Message::SplitModelViewport(true),
crate::tr!("viewport-split-horizontal"),
"VPORTS 2H",
));
// Drag handle + close: only meaningful with more than one model tile.
// The handle is a `mouse_area` (not a button) so it fires on press-DOWN,
// letting the drag continue onto the target pane to swap them (a button
@@ -146,11 +198,17 @@ pub(super) fn viewport_controls<'a>(
)
.interaction(iced::mouse::Interaction::Grab)
.on_press(Message::PaneMoveStart);
let drag = viewport_tooltip(drag, crate::tr!("viewport-move"), "VPORTS");
bar = bar
.push(sep())
.push(drag)
.push(sep())
.push(danger_btn(crate::ui::icons::CLOSE, Message::CloseModelViewport));
.push(danger_btn(
crate::ui::icons::CLOSE,
Message::CloseModelViewport,
crate::tr!("viewport-close"),
"VPORTS SINGLE",
));
}
}
+8
View File
@@ -28,6 +28,14 @@ pub use collapse::CollapseMode;
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
use crate::t;
pub(crate) fn tooltip_content(text: String) -> Element<'static, Message> {
widgets::make_tip(text)
}
pub(crate) fn tooltip_style(theme: &Theme) -> container::Style {
widgets::tip_style(theme)
}
// ── Ribbon state ───────────────────────────────────────────────────────────
pub struct Ribbon {