feat(command-line): resize history panel

Refs #456
This commit is contained in:
Hakan Seven committed 2026-08-04 16:54:59 +03:00
1 parent 282a5176af
commit b0806679c9
6 files changed
+180 -23

No files matched your search

+11
View File
@@ -320,6 +320,9 @@ pub(super) struct OpenCADStudio {
/// the log can be drag-selected across lines and copied (issue #232).
/// Rebuilt from the history each time the dropdown is opened.
history_content: iced::widget::text_editor::Content,
/// Pointer state while the command-history panel's top edge is dragged.
command_history_resizing: bool,
command_history_drag_last: Option<Point>,
status_bar: StatusBar,
cursor_pos: Point,
vp_size: (f32, f32),
@@ -1808,6 +1811,12 @@ pub enum Message {
CommandHistoryNext,
/// Toggle the dropdown listing the full command-line history.
CommandHistoryToggle,
/// Grab/move/release the expanded history panel's top resize edge.
CommandHistoryResizeGrab,
CommandHistoryResizeMove(Point),
CommandHistoryResizeRelease,
/// Restore the expanded history panel to its default height.
CommandHistoryHeightReset,
/// Start dragging the Layer Manager's Name-column divider.
LayerNameColGrab,
/// Toggle the persistent literal-space mode (the `>` button): while on,
@@ -2837,6 +2846,8 @@ impl OpenCADStudio {
properties_drag_last: None,
properties_dock_preview: None,
history_content: iced::widget::text_editor::Content::new(),
command_history_resizing: false,
command_history_drag_last: None,
status_bar: StatusBar::new(),
cursor_pos: Point::ORIGIN,
vp_size: (1280.0, 720.0),
+3
View File
@@ -116,6 +116,8 @@ pub struct UserSettings {
/// instead of submitting (as if every line started with `>`), until the
/// user toggles it back off.
pub literal_spaces: bool,
/// Height of the expanded command-history editor in logical pixels.
pub command_history_height: f32,
/// Running object-snap set + master toggle as an `$OSMODE`-style bitmask.
/// App-level, not per-drawing: modern DWG (R2000+) has no file slot for
/// OSMODE (it moved to the registry), so the set follows the user. A
@@ -167,6 +169,7 @@ impl Default for UserSettings {
disabled_plugins: Vec::new(),
plugin_repos: Vec::new(),
literal_spaces: false,
command_history_height: crate::ui::command_line::HISTORY_HEIGHT_DEFAULT,
// Snapper::default(): END|MID|CEN|NODE|QUAD|INT|NEA (575), master
// off (suppress bit 16384).
osmode: 575 | OSMODE_SUPPRESS,
+14 -1
View File
@@ -274,6 +274,7 @@ impl OpenCADStudio {
},
plugin_repos: self.plugin_repos.clone(),
literal_spaces: self.command_line.literal_spaces,
command_history_height: self.command_line.history_height,
osmode: crate::app::settings::osmode_from_snaps(
self.snapper.enabled.iter(),
self.snapper.snap_enabled,
@@ -304,6 +305,14 @@ impl OpenCADStudio {
self.disabled_plugins = s.disabled_plugins.iter().cloned().collect();
self.plugin_repos = s.plugin_repos.clone();
self.command_line.literal_spaces = s.literal_spaces;
self.command_line.history_height = if s.command_history_height.is_finite() {
s.command_history_height.clamp(
crate::ui::command_line::HISTORY_HEIGHT_MIN,
crate::ui::command_line::HISTORY_HEIGHT_MAX,
)
} else {
crate::ui::command_line::HISTORY_HEIGHT_DEFAULT
};
let (modes, snap_enabled) = crate::app::settings::snaps_from_osmode(s.osmode);
self.snapper.enabled = modes.into_iter().collect();
self.snapper.snap_enabled = snap_enabled;
@@ -587,7 +596,11 @@ impl OpenCADStudio {
/// Back-compat name for the many "a preference changed, persist it" sites.
pub(in crate::app) fn persist_settings_if_changed(&mut self) {
self.save_config();
// Keep resize feedback live without writing settings on every pointer
// move. The release message saves the final height once.
if !self.command_history_resizing {
self.save_config();
}
}
/// Record that the one-time default-association prompt has been answered and
+66 -8
View File
@@ -111,10 +111,10 @@ impl OpenCADStudio {
if self.history_content.text() == latest {
return;
}
use iced::widget::text_editor::{Action, Motion};
self.history_content = iced::widget::text_editor::Content::with_text(&latest);
self.history_content
.perform(Action::Move(Motion::DocumentEnd));
// The outer scrollable is anchored to the newest lines. Leaving the
// editor cursor at its initial position also keeps horizontal scroll at
// zero, so the first glyph of each line cannot be clipped.
}
/// Close the active in-canvas modal (Plan B), mirroring what closing the
@@ -1565,10 +1565,56 @@ impl OpenCADStudio {
Message::CommandHistoryToggle => {
self.command_line.toggle_history();
if !self.command_line.history_open {
self.command_history_resizing = false;
self.command_history_drag_last = None;
}
self.sync_open_command_history();
Task::none()
}
Message::CommandHistoryResizeGrab => {
if self.command_line.history_open {
self.command_history_resizing = true;
self.command_history_drag_last = None;
}
Task::none()
}
Message::CommandHistoryResizeMove(point) => {
if self.command_history_resizing {
if let Some(last) = self.command_history_drag_last {
let dy = point.y - last.y;
let max_height =
crate::ui::command_line::history_max_height(self.win_size.1);
self.command_line.history_height = (self.command_line.history_height - dy)
.clamp(
crate::ui::command_line::HISTORY_HEIGHT_MIN,
max_height,
);
}
self.command_history_drag_last = Some(point);
}
Task::none()
}
Message::CommandHistoryResizeRelease => {
let changed = self.command_history_resizing;
self.command_history_resizing = false;
self.command_history_drag_last = None;
if changed {
self.save_config();
}
Task::none()
}
Message::CommandHistoryHeightReset => {
self.command_line.history_height =
crate::ui::command_line::HISTORY_HEIGHT_DEFAULT;
self.save_config();
Task::none()
}
Message::CommandHistoryCopy => {
let text = self.command_line.history_plain_text();
if text.is_empty() {
@@ -1599,12 +1645,24 @@ impl OpenCADStudio {
}
Message::CommandHistoryEdit(action) => {
// Read-only: drop edits, keep selection / cursor / scroll so
// the user can still highlight and Ctrl+C the log.
if !action.is_edit() {
self.history_content.perform(action);
// Read-only: drop edits, keep selection/cursor actions, and
// route wheel input to the outer scrollbar. The text editor
// remains one selectable buffer while the scrollbar stays
// visible and draggable.
if let iced::widget::text_editor::Action::Scroll { lines } = action {
iced::widget::operation::scroll_by(
iced::widget::Id::new(crate::ui::command_line::HISTORY_SCROLL_ID),
iced::widget::scrollable::AbsoluteOffset {
x: 0.0,
y: lines as f32 * 14.0,
},
)
} else {
if !action.is_edit() {
self.history_content.perform(action);
}
Task::none()
}
Task::none()
}
Message::CommandSuggestionPick(cmd) => {
+11
View File
@@ -1499,6 +1499,7 @@ impl OpenCADStudio {
allow_autocomplete,
dyn_capturing,
&self.history_content,
self.win_size.1,
);
let center_stack: Element<'_, Message> = if tab.is_start {
column![
@@ -1534,6 +1535,16 @@ impl OpenCADStudio {
.into()
};
let center_stack: Element<'_, Message> = if self.command_history_resizing {
mouse_area(center_stack)
.on_move(Message::CommandHistoryResizeMove)
.on_release(Message::CommandHistoryResizeRelease)
.interaction(iced::mouse::Interaction::ResizingVertically)
.into()
} else {
center_stack
};
let main_ui = container({
// Clean-screen mode drops the ribbon for a full-canvas view; the
// status bar stays so the mode can be toggled back off.