mirror of
https://github.com/rust-kotlin/ashell.git
synced 2026-09-22 00:00:59 +00:00
feat: support custom terminal cursor style and blinking setting
This commit is contained in:
@@ -67,6 +67,11 @@ terminal_font_size: "Terminal Font Size"
|
||||
system_default: "System Default"
|
||||
ui_font_family: "UI Font"
|
||||
terminal_font_family: "Terminal Font"
|
||||
cursor_style: "Cursor Style"
|
||||
cursor_style_default: "Default"
|
||||
cursor_style_blink: "Blink"
|
||||
cursor_style_beam: "Line"
|
||||
cursor_style_beam_blink: "Line Blink"
|
||||
monitoring_position: "Monitoring Position"
|
||||
position_bottom: "Bottom"
|
||||
position_sidebar: "Sidebar"
|
||||
|
||||
@@ -68,6 +68,11 @@ terminal_font_size: "终端字体大小"
|
||||
system_default: "系统默认"
|
||||
ui_font_family: "界面字体"
|
||||
terminal_font_family: "终端字体"
|
||||
cursor_style: "光标样式"
|
||||
cursor_style_default: "默认"
|
||||
cursor_style_blink: "闪烁"
|
||||
cursor_style_beam: "细线"
|
||||
cursor_style_beam_blink: "细线闪烁"
|
||||
monitoring_position: "系统监控显示位置"
|
||||
position_bottom: "底部"
|
||||
position_sidebar: "侧边栏"
|
||||
|
||||
@@ -1521,6 +1521,58 @@ impl Ashell {
|
||||
})
|
||||
)
|
||||
)
|
||||
.item(
|
||||
SettingItem::new(
|
||||
t!("cursor_style").to_string(),
|
||||
SettingField::render({
|
||||
let view = view_clone_for_general.clone();
|
||||
move |_, _window, cx| {
|
||||
use crate::session::config::CursorStyle;
|
||||
let current = view.read(cx).cursor_style;
|
||||
Button::new("cursor-style-dropdown")
|
||||
.small()
|
||||
.icon(IconName::ChevronsUpDown)
|
||||
.label(match current {
|
||||
CursorStyle::Default => t!("cursor_style_default").to_string(),
|
||||
CursorStyle::Blink => t!("cursor_style_blink").to_string(),
|
||||
CursorStyle::Beam => t!("cursor_style_beam").to_string(),
|
||||
CursorStyle::BeamBlink => t!("cursor_style_beam_blink").to_string(),
|
||||
})
|
||||
.dropdown_menu_with_anchor(Anchor::BottomRight, {
|
||||
let view = view.clone();
|
||||
move |mut menu, window, cx| {
|
||||
use crate::session::config::CursorStyle;
|
||||
let current = view.read(cx).cursor_style;
|
||||
menu = menu.min_w(160.).max_h(px(320.)).scrollable(true);
|
||||
for style in [
|
||||
CursorStyle::Default,
|
||||
CursorStyle::Blink,
|
||||
CursorStyle::Beam,
|
||||
CursorStyle::BeamBlink,
|
||||
] {
|
||||
let checked = style == current;
|
||||
let label = match style {
|
||||
CursorStyle::Default => t!("cursor_style_default").to_string(),
|
||||
CursorStyle::Blink => t!("cursor_style_blink").to_string(),
|
||||
CursorStyle::Beam => t!("cursor_style_beam").to_string(),
|
||||
CursorStyle::BeamBlink => t!("cursor_style_beam_blink").to_string(),
|
||||
};
|
||||
menu = menu.item(
|
||||
PopupMenuItem::new(label)
|
||||
.checked(checked)
|
||||
.on_click(window.listener_for(&view, move |this, _, _window, cx| {
|
||||
this.change_cursor_style(style, cx);
|
||||
}))
|
||||
);
|
||||
}
|
||||
menu
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.group(
|
||||
SettingGroup::new()
|
||||
|
||||
+14
-1
@@ -275,6 +275,7 @@ pub(crate) struct Ashell {
|
||||
pub(crate) status: SharedString,
|
||||
pub(crate) config: ConfigStore,
|
||||
pub(crate) active_title_bar_style: crate::session::config::TitleBarStyle,
|
||||
pub(crate) cursor_style: crate::session::config::CursorStyle,
|
||||
pub(crate) system_sampler: SystemSampler,
|
||||
pub(crate) recording_action: Option<String>,
|
||||
pub(crate) active_dialog: Option<DialogKind>,
|
||||
@@ -545,6 +546,7 @@ impl Ashell {
|
||||
dark_theme_name,
|
||||
ui_font_size: config.ui_font_size(),
|
||||
terminal_font_size: config.terminal_font_size(),
|
||||
cursor_style: config.cursor_style(),
|
||||
ui_font_family,
|
||||
terminal_font_family,
|
||||
tabs: Vec::new(),
|
||||
@@ -699,6 +701,7 @@ impl Ashell {
|
||||
pub(crate) fn start_event_pump(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.spawn_in(window, async move |this, mut cx| {
|
||||
let mut idle_frames = 0u32;
|
||||
let mut last_blink_time = std::time::Instant::now();
|
||||
loop {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(16))
|
||||
@@ -710,9 +713,19 @@ impl Ashell {
|
||||
changed = this.drain_backend_events(window, cx);
|
||||
system_sampled = this.sample_system_if_due();
|
||||
this.sync_theme_if_due(cx);
|
||||
if changed || system_sampled {
|
||||
let is_blinking = matches!(
|
||||
this.cursor_style,
|
||||
crate::session::config::CursorStyle::Blink
|
||||
| crate::session::config::CursorStyle::BeamBlink
|
||||
);
|
||||
let now = std::time::Instant::now();
|
||||
let blink_due = is_blinking && now.duration_since(last_blink_time) >= std::time::Duration::from_millis(600);
|
||||
if changed || system_sampled || blink_due {
|
||||
cx.notify();
|
||||
idle_frames = 0;
|
||||
if blink_due {
|
||||
last_blink_time = now;
|
||||
}
|
||||
} else {
|
||||
idle_frames += 1;
|
||||
if idle_frames >= 60 {
|
||||
|
||||
@@ -128,6 +128,16 @@ pub enum TitleBarStyle {
|
||||
Integrated,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum CursorStyle {
|
||||
#[default]
|
||||
Default,
|
||||
Blink,
|
||||
Beam,
|
||||
BeamBlink,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigFile {
|
||||
#[serde(default = "default_follow_system_theme")]
|
||||
@@ -155,6 +165,8 @@ pub struct ConfigFile {
|
||||
#[serde(default)]
|
||||
pub title_bar_style: TitleBarStyle,
|
||||
#[serde(default)]
|
||||
pub cursor_style: CursorStyle,
|
||||
#[serde(default)]
|
||||
pub sessions: Vec<Session>,
|
||||
#[serde(default)]
|
||||
pub window_bounds: Option<SavedWindowBounds>,
|
||||
@@ -249,6 +261,7 @@ impl Default for ConfigFile {
|
||||
ui_font_family: default_ui_font_family(),
|
||||
terminal_font_family: default_terminal_font_family(),
|
||||
title_bar_style: TitleBarStyle::default(),
|
||||
cursor_style: CursorStyle::default(),
|
||||
sessions: Vec::new(),
|
||||
window_bounds: None,
|
||||
workspace_panels: None,
|
||||
@@ -603,6 +616,14 @@ impl ConfigStore {
|
||||
self.cache.title_bar_style = style;
|
||||
}
|
||||
|
||||
pub fn cursor_style(&self) -> CursorStyle {
|
||||
self.cache.cursor_style
|
||||
}
|
||||
|
||||
pub fn set_cursor_style(&mut self, style: CursorStyle) {
|
||||
self.cache.cursor_style = style;
|
||||
}
|
||||
|
||||
pub fn show_hidden_files(&self) -> bool {
|
||||
self.cache.show_hidden_files
|
||||
}
|
||||
|
||||
@@ -297,6 +297,19 @@ impl Ashell {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn change_cursor_style(
|
||||
&mut self,
|
||||
style: crate::session::config::CursorStyle,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.cursor_style = style;
|
||||
self.config.set_cursor_style(style);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save cursor style: {err:#}");
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn reset_layout(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.config.set_layout_state(None, None, None);
|
||||
let _ = self.config.save();
|
||||
|
||||
+29
-5
@@ -464,11 +464,35 @@ impl TerminalElement {
|
||||
}
|
||||
|
||||
fn cursor_layout(&self, cx: &App) -> Option<CursorLayout> {
|
||||
self.snapshot.cursor.map(|cursor| CursorLayout {
|
||||
row: cursor.row,
|
||||
col: cursor.col,
|
||||
shape: cursor.shape,
|
||||
color: cx.theme().primary,
|
||||
use crate::session::config::CursorStyle;
|
||||
let cursor_style = self.view.read(cx).cursor_style;
|
||||
let show_cursor = match cursor_style {
|
||||
CursorStyle::Blink | CursorStyle::BeamBlink => {
|
||||
if let Ok(duration) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
|
||||
(duration.as_millis() / 600) % 2 == 0
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
|
||||
self.snapshot.cursor.map(|cursor| {
|
||||
let mut shape = match cursor_style {
|
||||
CursorStyle::Default => cursor.shape,
|
||||
CursorStyle::Blink => CursorShape::Block,
|
||||
CursorStyle::Beam => CursorShape::Beam,
|
||||
CursorStyle::BeamBlink => CursorShape::Beam,
|
||||
};
|
||||
if !show_cursor {
|
||||
shape = CursorShape::Hidden;
|
||||
}
|
||||
CursorLayout {
|
||||
row: cursor.row,
|
||||
col: cursor.col,
|
||||
shape,
|
||||
color: cx.theme().primary,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user