Merge branch 'master' into primary
This commit is contained in:
commit
d38ac0a0f7
10 changed files with 1509 additions and 426 deletions
528
Cargo.lock
generated
528
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
|||
name = "cosmic-term"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.71"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
|
@ -27,7 +28,6 @@ git = "https://github.com/pop-os/cosmic-text.git"
|
|||
git = "https://github.com/pop-os/libcosmic.git"
|
||||
default-features = false
|
||||
features = ["tokio", "winit"]
|
||||
#path = "../libcosmic"
|
||||
|
||||
[features]
|
||||
default = ["wgpu"]
|
||||
|
|
|
|||
|
|
@ -9,17 +9,29 @@ theme = Theme
|
|||
match-desktop = Match desktop
|
||||
dark = Dark
|
||||
light = Light
|
||||
syntax-dark = Syntax dark
|
||||
syntax-light = Syntax light
|
||||
syntax-dark = Color scheme dark
|
||||
syntax-light = Color scheme light
|
||||
default-zoom-step = Zoom steps
|
||||
|
||||
### Font
|
||||
font = Font
|
||||
advanced-font-settings = Advanced Font Settings
|
||||
default-font = Default font
|
||||
default-font-stretch = Default font stretch
|
||||
default-font-weight = Default font weight
|
||||
default-dim-font-weight = Default dim font weight
|
||||
default-bold-font-weight = Default bold font weight
|
||||
use-bright-bold = Use bright colors with bold text
|
||||
default-font-size = Default font size
|
||||
default-zoom-step = Default zoom step
|
||||
default-font = Font
|
||||
default-font-size = Font size
|
||||
default-font-stretch = Font stretch
|
||||
default-font-weight = Normal font weight
|
||||
default-dim-font-weight = Dim font weight
|
||||
default-bold-font-weight = Bold font weight
|
||||
use-bright-bold = Make bold text brighter
|
||||
|
||||
### Splits
|
||||
splits = Splits
|
||||
focus-follow-mouse = Typing focus follows mouse
|
||||
|
||||
### Advanced
|
||||
advanced = Advanced
|
||||
show-headerbar = Show header
|
||||
show-header-description = Reveal the header from the right-click menu.
|
||||
|
||||
# Find
|
||||
find-placeholder = Find...
|
||||
|
|
@ -53,6 +65,3 @@ split-horizontal = Split horizontal
|
|||
split-vertical = Split vertical
|
||||
pane-toggle-maximize = Toggle maximized
|
||||
menu-settings = Settings...
|
||||
|
||||
# Context menu
|
||||
show-headerbar = Show header bar
|
||||
|
|
|
|||
50
i18n/ja/cosmic_term.ftl
Normal file
50
i18n/ja/cosmic_term.ftl
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Context Pages
|
||||
|
||||
## Settings
|
||||
settings = 設定
|
||||
|
||||
### Appearance
|
||||
appearance = 外観
|
||||
theme = テーマ
|
||||
match-desktop = デスクトップに合わす
|
||||
dark = 暗い
|
||||
light = 明かり
|
||||
syntax-dark = 暗いシンタックス
|
||||
syntax-light = 明かりシンタックス
|
||||
advanced-font-settings = 詳細なフォント設定
|
||||
default-font = デフォルトフォント
|
||||
default-font-stretch = デフォルトのフォント幅
|
||||
default-font-weight = デフォルトのフォントの太さ
|
||||
default-dim-font-weight = デフォルトの暗いフォントの太さ
|
||||
default-bold-font-weight = デフォルトの太字の太さ
|
||||
use-bright-bold = 太字を明るい色で表示する
|
||||
default-font-size = デフォルトのフォントサイズ
|
||||
default-zoom-step =ズームの間隔
|
||||
|
||||
# Find
|
||||
find-placeholder = 検索...
|
||||
find-previous = 前を検索
|
||||
find-next = 次を検索
|
||||
|
||||
# Menu
|
||||
|
||||
## File
|
||||
file = ファイル
|
||||
new-tab = 新しいタブ
|
||||
new-window = 新しいウィンドウ
|
||||
close-tab = タブを閉じる
|
||||
quit = 終了
|
||||
|
||||
## Edit
|
||||
edit = 編集
|
||||
copy = コピー
|
||||
paste = 貼り付け
|
||||
select-all = すべて選択
|
||||
find = 検索
|
||||
|
||||
## View
|
||||
view = 表示
|
||||
menu-settings = 設定...
|
||||
|
||||
# Context menu
|
||||
show-headerbar = ヘッダーバーを表す
|
||||
|
|
@ -43,6 +43,7 @@ pub struct Config {
|
|||
pub use_bright_bold: bool,
|
||||
pub syntax_theme_dark: String,
|
||||
pub syntax_theme_light: String,
|
||||
pub focus_follow_mouse: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
|
@ -60,6 +61,7 @@ impl Default for Config {
|
|||
use_bright_bold: false,
|
||||
syntax_theme_dark: "COSMIC Dark".to_string(),
|
||||
syntax_theme_light: "COSMIC Light".to_string(),
|
||||
focus_follow_mouse: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
231
src/main.rs
231
src/main.rs
|
|
@ -15,7 +15,7 @@ use cosmic::{
|
|||
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
||||
mouse::{Button as MouseButton, Event as MouseEvent},
|
||||
subscription::{self, Subscription},
|
||||
window, Alignment, Event, Length, Padding, Point,
|
||||
window, Alignment, Color, Event, Length, Limits, Padding, Point,
|
||||
},
|
||||
style,
|
||||
widget::{self, button, container, pane_grid, segmented_button, PaneGrid},
|
||||
|
|
@ -26,13 +26,14 @@ use std::{
|
|||
any::TypeId,
|
||||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
env, process,
|
||||
sync::Mutex,
|
||||
sync::{atomic::Ordering, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use config::{AppTheme, Config, CONFIG_VERSION};
|
||||
mod config;
|
||||
mod mouse_reporter;
|
||||
|
||||
use icon_cache::IconCache;
|
||||
mod icon_cache;
|
||||
|
|
@ -141,8 +142,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
settings = settings.client_decorations(false);
|
||||
}
|
||||
|
||||
//TODO: allow size limits on iced_winit
|
||||
//settings = settings.size_limits(Limits::NONE.min_width(400.0).min_height(200.0));
|
||||
settings = settings.size_limits(Limits::NONE.min_width(360.0).min_height(180.0));
|
||||
|
||||
let flags = Flags {
|
||||
config_handler,
|
||||
|
|
@ -267,6 +267,7 @@ pub enum Message {
|
|||
PaneDragged(pane_grid::DragEvent),
|
||||
PaneResized(pane_grid::ResizeEvent),
|
||||
Modifiers(Modifiers),
|
||||
MouseEnter(pane_grid::Pane),
|
||||
Paste(Option<segmented_button::Entity>),
|
||||
PastePrimary(Option<segmented_button::Entity>),
|
||||
PasteValue(Option<segmented_button::Entity>, String),
|
||||
|
|
@ -274,7 +275,7 @@ pub enum Message {
|
|||
UseBrightBold(bool),
|
||||
ShowHeaderBar(bool),
|
||||
SyntaxTheme(usize, bool),
|
||||
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
||||
SystemThemeChange,
|
||||
TabActivate(segmented_button::Entity),
|
||||
TabActivateJump(usize),
|
||||
TabClose(Option<segmented_button::Entity>),
|
||||
|
|
@ -292,6 +293,7 @@ pub enum Message {
|
|||
ZoomIn,
|
||||
ZoomOut,
|
||||
ZoomReset,
|
||||
FocusFollowMouse(bool),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
|
|
@ -530,6 +532,67 @@ impl App {
|
|||
.iter()
|
||||
.position(|zoom_step| zoom_step == &self.config.font_size_zoom_step_mul_100);
|
||||
|
||||
let appearance_section = widget::settings::view_section(fl!("appearance"))
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
|
||||
&self.app_themes,
|
||||
Some(app_theme_selected),
|
||||
move |index| {
|
||||
Message::AppTheme(match index {
|
||||
1 => AppTheme::Dark,
|
||||
2 => AppTheme::Light,
|
||||
_ => AppTheme::System,
|
||||
})
|
||||
},
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
//TODO: rename to color-scheme-dark?
|
||||
widget::settings::item::builder(fl!("syntax-dark")).control(widget::dropdown(
|
||||
&self.theme_names,
|
||||
dark_selected,
|
||||
move |index| Message::SyntaxTheme(index, true),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
//TODO: rename to color-scheme-light?
|
||||
widget::settings::item::builder(fl!("syntax-light")).control(widget::dropdown(
|
||||
&self.theme_names,
|
||||
light_selected,
|
||||
move |index| Message::SyntaxTheme(index, false),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-zoom-step")).control(
|
||||
widget::dropdown(&self.zoom_step_names, zoom_step_selected, |index| {
|
||||
Message::DefaultZoomStep(index)
|
||||
}),
|
||||
),
|
||||
);
|
||||
//TODO: background opacity
|
||||
|
||||
let mut font_section = widget::settings::view_section(fl!("font"))
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-font")).control(widget::dropdown(
|
||||
&self.font_names,
|
||||
font_selected,
|
||||
|index| Message::DefaultFont(index),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-font-size")).control(
|
||||
widget::dropdown(&self.font_size_names, font_size_selected, |index| {
|
||||
Message::DefaultFontSize(index)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("advanced-font-settings")).toggler(
|
||||
self.show_advanced_font_settings,
|
||||
Message::ShowAdvancedFontSettings,
|
||||
),
|
||||
);
|
||||
|
||||
let advanced_font_settings = || {
|
||||
let section = widget::settings::view_section("")
|
||||
.add(
|
||||
|
|
@ -567,6 +630,10 @@ impl App {
|
|||
|index| Message::DefaultBoldFontWeight(index),
|
||||
),
|
||||
),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("use-bright-bold"))
|
||||
.toggler(self.config.use_bright_bold, Message::UseBrightBold),
|
||||
);
|
||||
let padding = Padding {
|
||||
top: 0.0,
|
||||
|
|
@ -577,77 +644,28 @@ impl App {
|
|||
widget::container(section).padding(padding)
|
||||
};
|
||||
|
||||
let mut settings_view = widget::settings::view_section(fl!("appearance"))
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
|
||||
&self.app_themes,
|
||||
Some(app_theme_selected),
|
||||
move |index| {
|
||||
Message::AppTheme(match index {
|
||||
1 => AppTheme::Dark,
|
||||
2 => AppTheme::Light,
|
||||
_ => AppTheme::System,
|
||||
})
|
||||
},
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("syntax-dark")).control(widget::dropdown(
|
||||
&self.theme_names,
|
||||
dark_selected,
|
||||
move |index| Message::SyntaxTheme(index, true),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("syntax-light")).control(widget::dropdown(
|
||||
&self.theme_names,
|
||||
light_selected,
|
||||
move |index| Message::SyntaxTheme(index, false),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-font")).control(widget::dropdown(
|
||||
&self.font_names,
|
||||
font_selected,
|
||||
|index| Message::DefaultFont(index),
|
||||
)),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("advanced-font-settings")).toggler(
|
||||
self.show_advanced_font_settings,
|
||||
Message::ShowAdvancedFontSettings,
|
||||
),
|
||||
);
|
||||
|
||||
if self.show_advanced_font_settings {
|
||||
settings_view = settings_view.add(advanced_font_settings());
|
||||
font_section = font_section.add(advanced_font_settings());
|
||||
}
|
||||
|
||||
let settings_view = settings_view
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("use-bright-bold"))
|
||||
.toggler(self.config.use_bright_bold, Message::UseBrightBold),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-font-size")).control(
|
||||
widget::dropdown(&self.font_size_names, font_size_selected, |index| {
|
||||
Message::DefaultFontSize(index)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("default-zoom-step")).control(
|
||||
widget::dropdown(&self.zoom_step_names, zoom_step_selected, |index| {
|
||||
Message::DefaultZoomStep(index)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.add(
|
||||
widget::settings::item::builder(fl!("show-headerbar"))
|
||||
.toggler(self.config.show_headerbar, Message::ShowHeaderBar),
|
||||
);
|
||||
let splits_section = widget::settings::view_section(fl!("splits")).add(
|
||||
widget::settings::item::builder(fl!("focus-follow-mouse"))
|
||||
.toggler(self.config.focus_follow_mouse, Message::FocusFollowMouse),
|
||||
);
|
||||
|
||||
widget::settings::view_column(vec![settings_view.into()]).into()
|
||||
let advanced_section = widget::settings::view_section(fl!("advanced")).add(
|
||||
widget::settings::item::builder(fl!("show-headerbar"))
|
||||
.description(fl!("show-header-description"))
|
||||
.toggler(self.config.show_headerbar, Message::ShowHeaderBar),
|
||||
);
|
||||
|
||||
widget::settings::view_column(vec![
|
||||
appearance_section.into(),
|
||||
font_section.into(),
|
||||
splits_section.into(),
|
||||
advanced_section.into(),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
||||
fn create_and_focus_new_terminal(&mut self, pane: pane_grid::Pane) {
|
||||
|
|
@ -719,8 +737,7 @@ impl Application for App {
|
|||
|
||||
/// Creates the application, and optionally emits command on initialize.
|
||||
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
//TODO: fix window resizing interfering with scrolling when not using content container
|
||||
//core.window.content_container = false;
|
||||
core.window.content_container = false;
|
||||
core.window.show_headerbar = flags.config.show_headerbar;
|
||||
|
||||
// Update font name from config
|
||||
|
|
@ -1079,6 +1096,10 @@ impl Application for App {
|
|||
Message::Modifiers(modifiers) => {
|
||||
self.modifiers = modifiers;
|
||||
}
|
||||
Message::MouseEnter(pane) => {
|
||||
self.pane_model.focus = pane;
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::PaneClicked(pane) => {
|
||||
self.pane_model.focus = pane;
|
||||
return self.update_title(Some(pane));
|
||||
|
|
@ -1163,7 +1184,13 @@ impl Application for App {
|
|||
return self.save_config();
|
||||
}
|
||||
}
|
||||
Message::SystemThemeModeChange(_theme_mode) => {
|
||||
Message::FocusFollowMouse(focus_follow_mouse) => {
|
||||
if focus_follow_mouse != self.config.focus_follow_mouse {
|
||||
self.config.focus_follow_mouse = focus_follow_mouse;
|
||||
return self.save_config();
|
||||
}
|
||||
}
|
||||
Message::SystemThemeChange => {
|
||||
return self.update_config();
|
||||
}
|
||||
Message::SyntaxTheme(index, dark) => match self.theme_names.get(index) {
|
||||
|
|
@ -1413,9 +1440,28 @@ impl Application for App {
|
|||
vec![menu_bar(&self.key_binds).into()]
|
||||
}
|
||||
|
||||
fn header_end(&self) -> Vec<Element<Self::Message>> {
|
||||
let cosmic_theme::Spacing { space_xxs, .. } = self.core().system_theme().cosmic().spacing;
|
||||
vec![widget::button(widget::icon::from_name("list-add-symbolic"))
|
||||
.on_press(Message::TabNew)
|
||||
.padding(space_xxs)
|
||||
.style(style::Button::Icon)
|
||||
.into()]
|
||||
}
|
||||
|
||||
/// Creates a view after each update.
|
||||
fn view(&self) -> Element<Self::Message> {
|
||||
let cosmic_theme::Spacing { space_xxs, .. } = self.core().system_theme().cosmic().spacing;
|
||||
let cosmic_theme = self.core().system_theme().cosmic();
|
||||
let cosmic_theme::Spacing { space_xxs, .. } = cosmic_theme.spacing;
|
||||
{
|
||||
let color = Color::from(cosmic_theme.bg_color());
|
||||
let bytes = color.into_rgba8();
|
||||
let data = (bytes[2] as u32)
|
||||
| ((bytes[1] as u32) << 8)
|
||||
| ((bytes[0] as u32) << 16)
|
||||
| 0xFF000000;
|
||||
terminal::WINDOW_BG_COLOR.store(data, Ordering::SeqCst);
|
||||
}
|
||||
let pane_grid = PaneGrid::new(&self.pane_model.panes, |pane, tab_model, _is_maximized| {
|
||||
let mut tab_column = widget::column::with_capacity(1);
|
||||
|
||||
|
|
@ -1442,7 +1488,7 @@ impl Application for App {
|
|||
.unwrap_or_else(widget::Id::unique);
|
||||
match tab_model.data::<Mutex<Terminal>>(entity) {
|
||||
Some(terminal) => {
|
||||
let terminal_box = terminal_box(terminal)
|
||||
let mut terminal_box = terminal_box(terminal)
|
||||
.id(terminal_id)
|
||||
.on_context_menu(move |position_opt| {
|
||||
Message::TabContextMenu(entity, position_opt)
|
||||
|
|
@ -1451,6 +1497,11 @@ impl Application for App {
|
|||
Message::MiddleClick(pane, Some(entity_middle_click))
|
||||
});
|
||||
|
||||
if self.config.focus_follow_mouse {
|
||||
terminal_box =
|
||||
terminal_box.on_mouse_enter(move || Message::MouseEnter(pane));
|
||||
}
|
||||
|
||||
let context_menu = {
|
||||
let terminal = terminal.lock().unwrap();
|
||||
terminal.context_menu
|
||||
|
|
@ -1543,6 +1594,7 @@ impl Application for App {
|
|||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(space_xxs)
|
||||
.style(style::Container::Background)
|
||||
.into()
|
||||
}
|
||||
|
||||
|
|
@ -1550,6 +1602,7 @@ impl Application for App {
|
|||
struct ConfigSubscription;
|
||||
struct TerminalEventSubscription;
|
||||
struct ThemeSubscription;
|
||||
struct ThemeModeSubscription;
|
||||
|
||||
Subscription::batch([
|
||||
event::listen_with(|event, _status| match event {
|
||||
|
|
@ -1603,21 +1656,23 @@ impl Application for App {
|
|||
}
|
||||
Message::Config(update.config)
|
||||
}),
|
||||
cosmic_config::config_subscription::<_, cosmic_theme::ThemeMode>(
|
||||
cosmic_config::config_subscription::<_, cosmic_theme::Theme>(
|
||||
TypeId::of::<ThemeSubscription>(),
|
||||
if self.core.system_theme_mode().is_dark {
|
||||
cosmic_theme::DARK_THEME_ID
|
||||
} else {
|
||||
cosmic_theme::LIGHT_THEME_ID
|
||||
}
|
||||
.into(),
|
||||
cosmic_theme::Theme::version(),
|
||||
)
|
||||
.map(|_update| Message::SystemThemeChange),
|
||||
cosmic_config::config_subscription::<_, cosmic_theme::ThemeMode>(
|
||||
TypeId::of::<ThemeModeSubscription>(),
|
||||
cosmic_theme::THEME_MODE_ID.into(),
|
||||
cosmic_theme::ThemeMode::version(),
|
||||
)
|
||||
.map(|update| {
|
||||
if !update.errors.is_empty() {
|
||||
log::debug!(
|
||||
"errors loading theme mode {:?}: {:?}",
|
||||
update.keys,
|
||||
update.errors
|
||||
);
|
||||
}
|
||||
Message::SystemThemeModeChange(update.config)
|
||||
}),
|
||||
.map(|_update| Message::SystemThemeChange),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
238
src/mouse_reporter.rs
Normal file
238
src/mouse_reporter.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
use cosmic::{
|
||||
iced::mouse::{Event as MouseEvent, ScrollDelta},
|
||||
iced::{keyboard::Modifiers, mouse::Button, Event},
|
||||
};
|
||||
|
||||
use crate::terminal::Terminal;
|
||||
|
||||
const SCROLL_SPEED: u32 = 3;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MouseReporter {
|
||||
last_movment_x: Option<u32>,
|
||||
last_movment_y: Option<u32>,
|
||||
button: Option<Button>,
|
||||
}
|
||||
|
||||
impl MouseReporter {
|
||||
fn button_number(&self, button: Button) -> Option<u8> {
|
||||
match button {
|
||||
Button::Left => Some(0),
|
||||
Button::Middle => Some(1),
|
||||
Button::Right => Some(2),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
//Implemented according to
|
||||
//https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
pub fn normal_mouse_code(
|
||||
&mut self,
|
||||
event: Event,
|
||||
modifiers: &Modifiers,
|
||||
is_utf8: bool,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) -> Option<Vec<u8>> {
|
||||
//Buttons are handle slightly different between normal and sgr
|
||||
//for normal/utf8 the button release is always reported as button 3
|
||||
let Some(mut button) = (match event {
|
||||
Event::Mouse(MouseEvent::ButtonPressed(b)) => {
|
||||
self.button = Some(b);
|
||||
self.button_number(b)
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonReleased(_b)) => {
|
||||
self.button = None;
|
||||
Some(3)
|
||||
}
|
||||
Event::Mouse(MouseEvent::CursorMoved { .. }) => {
|
||||
//Button pressed is reported as button 32 + 0,1,2 and event code M
|
||||
//And only reported if a button is previously pressed
|
||||
if (self.last_movment_x, self.last_movment_y) == (Some(x), Some(y)) {
|
||||
return None;
|
||||
} else {
|
||||
self.last_movment_x = Some(x);
|
||||
self.last_movment_y = Some(y);
|
||||
}
|
||||
//It seems that we should add 32 to signal movement even for normal mode
|
||||
//On button-motion events, xterm adds 32 to the event code (the third
|
||||
//character, Cb).
|
||||
//For example, motion into cell x,y with button 1 down is reported as
|
||||
//CSI M @ CxCy ( @ = 32 + 0 (button 1) + 32 (motion indicator) ).
|
||||
self.button
|
||||
.and_then(|button| self.button_number(button))
|
||||
.map(|b| b + 32)
|
||||
}
|
||||
_ => None,
|
||||
}) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if modifiers.shift() {
|
||||
button += 4;
|
||||
}
|
||||
if modifiers.alt() {
|
||||
button += 8;
|
||||
}
|
||||
if modifiers.control() {
|
||||
button += 16;
|
||||
}
|
||||
|
||||
//Normal mode have a max of 223 (255 - 32), while utf8 extend this to 2015
|
||||
let max_point: usize = if is_utf8 { 2015 } else { 223 };
|
||||
if x as usize >= max_point || y as usize >= max_point {
|
||||
return None;
|
||||
}
|
||||
|
||||
let utf8_encode_and_append = |mut pos: u32, dest: &mut Vec<u8>| {
|
||||
pos += 1 + 32;
|
||||
let mut utf8 = [0; 2]; //This is large enough since we have a max of 2015
|
||||
dest.extend_from_slice(
|
||||
(char::from_u32(pos).unwrap()) //This unwrap and encode_utf8 is safe due to our
|
||||
//specific range, pos will max be 2047
|
||||
.encode_utf8(&mut utf8)
|
||||
.as_bytes(),
|
||||
);
|
||||
};
|
||||
|
||||
//SPEC: Likewise, Cb will be UTF-8 encoded, to reduce confusion with wheel mouse events.
|
||||
//Always, or only when the the cooardinates is used? No other terminal seems to do this a
|
||||
//all? Doing what they are doing for now.
|
||||
let mut buf: Vec<u8> = vec![b'\x1b', b'[', b'M', 32 + button];
|
||||
//Should we remove 32+button from previous line, and use this instead? Or only on >= 95
|
||||
//utf8_encode_and_append(32 + button, &mut buf);
|
||||
|
||||
//For utf8 spec say: For positions less than 95, the resulting output is identical under both modes.
|
||||
//But also: Under normal mouse mode, positions outside (160,94) result in byte pairs which can be interpreted as a single UTF-8
|
||||
if is_utf8 && x >= 95 {
|
||||
utf8_encode_and_append(x, &mut buf);
|
||||
} else {
|
||||
//SPEC: For positions less than 95, the resulting output is identical under both modes.
|
||||
buf.push(32 + 1 + x as u8);
|
||||
}
|
||||
|
||||
if is_utf8 && y >= 95 {
|
||||
utf8_encode_and_append(y, &mut buf);
|
||||
} else {
|
||||
//SPEC For positions less than 95, the resulting output is identical under both modes.
|
||||
buf.push(32 + 1 + y as u8);
|
||||
}
|
||||
Some(buf)
|
||||
}
|
||||
|
||||
pub fn sgr_mouse_code(
|
||||
&mut self,
|
||||
event: Event,
|
||||
modifiers: &Modifiers,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) -> Option<Vec<u8>> {
|
||||
let Some((button_no, event_code)) = (match event {
|
||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||
//Button pressed is reported as button 0,1,2 and event code M
|
||||
self.button = Some(button);
|
||||
Some((self.button_number(button), "M"))
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonReleased(button)) => {
|
||||
//Button pressed is reported as button 0,1,2 and event code m
|
||||
self.button = None;
|
||||
Some((self.button_number(button), "m"))
|
||||
}
|
||||
Event::Mouse(MouseEvent::CursorMoved { .. }) => {
|
||||
//Button pressed is reported as button 32 + 0,1,2 and event code M
|
||||
//And only reported if a button is previously pressed
|
||||
if (self.last_movment_x, self.last_movment_y) == (Some(x), Some(y)) {
|
||||
return None;
|
||||
} else {
|
||||
self.last_movment_x = Some(x);
|
||||
self.last_movment_y = Some(y);
|
||||
}
|
||||
self.button
|
||||
.map(|button| (self.button_number(button).map(|b| b + 32), "M"))
|
||||
}
|
||||
_ => None,
|
||||
}) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Some(mut button_no) = button_no {
|
||||
if modifiers.shift() {
|
||||
button_no += 4;
|
||||
}
|
||||
if modifiers.alt() {
|
||||
button_no += 8;
|
||||
}
|
||||
if modifiers.control() {
|
||||
button_no += 16;
|
||||
}
|
||||
let term_code = format!("\x1b[<{};{};{}{}", button_no, x + 1, y + 1, event_code);
|
||||
Some(term_code.as_bytes().to_vec())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_sgr_mouse_wheel_scroll(
|
||||
&self,
|
||||
terminal: &Terminal,
|
||||
term_cell_width: f32,
|
||||
term_cell_height: f32,
|
||||
delta: ScrollDelta,
|
||||
modifiers: &Modifiers,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) {
|
||||
let (delta_x, delta_y) = match delta {
|
||||
ScrollDelta::Lines { x, y } => (x, y),
|
||||
ScrollDelta::Pixels { x, y } => (x / term_cell_width, y / term_cell_height),
|
||||
};
|
||||
let (mut button_no, amount) = if delta_y > 0.0 {
|
||||
(64, delta_y.abs()) //Wheel UP
|
||||
} else if delta_y < 0.0 {
|
||||
(65, delta_y.abs()) //Wheel Down
|
||||
} else if delta_x < 0.0 {
|
||||
(66, delta_x.abs()) //Wheel Left
|
||||
} else if delta_x > 0.0 {
|
||||
(67, delta_x.abs()) //Wheel Right
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if modifiers.shift() {
|
||||
button_no += 4;
|
||||
}
|
||||
if modifiers.alt() {
|
||||
button_no += 8;
|
||||
}
|
||||
if modifiers.control() {
|
||||
button_no += 16;
|
||||
}
|
||||
let term_code = format!("\x1b[<{};{};{}M", button_no, x + 1, y + 1);
|
||||
for _ in 0..amount as u32 {
|
||||
terminal.input_no_scroll(term_code.as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
//Emulate mouse wheel scroll with up/down arrows. Using mouse spec uses
|
||||
//scroll-back and scroll-forw actions, which moves whole windows like page up/page down.
|
||||
pub fn report_mouse_wheel_as_arrows(
|
||||
&self,
|
||||
terminal: &Terminal,
|
||||
term_cell_width: f32,
|
||||
term_cell_height: f32,
|
||||
delta: ScrollDelta,
|
||||
) {
|
||||
let (_delta_x, delta_y) = match delta {
|
||||
ScrollDelta::Lines { x, y } => (x, y),
|
||||
ScrollDelta::Pixels { x, y } => (x / term_cell_width, y / term_cell_height),
|
||||
};
|
||||
//Send delta_y * SCROLL_SPEED number of Up/Down arrows
|
||||
for _ in 0..(delta_y.abs() as u32 * SCROLL_SPEED) {
|
||||
if delta_y > 0.0 {
|
||||
terminal.input_no_scroll(b"\x1B[A".as_slice())
|
||||
} else if delta_y < 0.0 {
|
||||
terminal.input_no_scroll(b"\x1B[B".as_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ use alacritty_terminal::{
|
|||
};
|
||||
use cosmic::{
|
||||
iced::advanced::graphics::text::font_system,
|
||||
iced::mouse::ScrollDelta,
|
||||
widget::{pane_grid, segmented_button},
|
||||
};
|
||||
use cosmic_text::{
|
||||
|
|
@ -27,14 +28,17 @@ use std::{
|
|||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
mem,
|
||||
sync::{Arc, Weak},
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Weak,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub use alacritty_terminal::grid::Scroll as TerminalScroll;
|
||||
|
||||
use crate::config::Config as AppConfig;
|
||||
use crate::{config::Config as AppConfig, mouse_reporter::MouseReporter};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Size {
|
||||
|
|
@ -97,14 +101,22 @@ fn as_dim(mut color: Color) -> Color {
|
|||
color
|
||||
}
|
||||
|
||||
pub static WINDOW_BG_COLOR: AtomicU32 = AtomicU32::new(0xFF000000);
|
||||
|
||||
fn convert_color(colors: &Colors, color: Color) -> cosmic_text::Color {
|
||||
let rgb = match color {
|
||||
Color::Named(named_color) => match colors[named_color] {
|
||||
Some(rgb) => rgb,
|
||||
None => {
|
||||
log::warn!("missing named color {:?}", named_color);
|
||||
Rgb::default()
|
||||
}
|
||||
None => match named_color {
|
||||
NamedColor::Background => {
|
||||
// Allow using an unset background
|
||||
return cosmic_text::Color(WINDOW_BG_COLOR.load(Ordering::SeqCst));
|
||||
}
|
||||
_ => {
|
||||
log::warn!("missing named color {:?}", named_color);
|
||||
Rgb::default()
|
||||
}
|
||||
},
|
||||
},
|
||||
Color::Spec(rgb) => rgb,
|
||||
Color::Indexed(index) => match colors[index as usize] {
|
||||
|
|
@ -189,6 +201,7 @@ pub struct Terminal {
|
|||
search_regex_opt: Option<RegexSearch>,
|
||||
search_value: String,
|
||||
pub metadata_set: IndexSet<Metadata>,
|
||||
mouse_reporter: MouseReporter,
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
|
|
@ -275,6 +288,7 @@ impl Terminal {
|
|||
search_regex_opt: None,
|
||||
search_value: String::new(),
|
||||
metadata_set,
|
||||
mouse_reporter: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +647,10 @@ impl Terminal {
|
|||
buffer.set_redraw(true);
|
||||
}
|
||||
|
||||
if buffer.lines[line_i].set_text(text.clone(), attrs_list.clone()) {
|
||||
// Tab skip/stop is handled by alacritty_terminal
|
||||
if buffer.lines[line_i]
|
||||
.set_text(text.replace('\t', " "), attrs_list.clone())
|
||||
{
|
||||
buffer.set_redraw(true);
|
||||
}
|
||||
line_i += 1;
|
||||
|
|
@ -773,6 +790,60 @@ impl Terminal {
|
|||
let term = self.term.lock();
|
||||
viewport_to_point(term.grid().display_offset(), point)
|
||||
}
|
||||
|
||||
pub fn report_mouse(
|
||||
&mut self,
|
||||
event: cosmic::iced::Event,
|
||||
modifiers: &cosmic::iced::keyboard::Modifiers,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) {
|
||||
let term_lock = self.term.lock();
|
||||
let mode = term_lock.mode();
|
||||
if mode.contains(TermMode::SGR_MOUSE) {
|
||||
if let Some(code) = self.mouse_reporter.sgr_mouse_code(event, modifiers, x, y) {
|
||||
self.input_no_scroll(code)
|
||||
}
|
||||
} else {
|
||||
if let Some(code) = self.mouse_reporter.normal_mouse_code(
|
||||
event,
|
||||
modifiers,
|
||||
mode.contains(TermMode::UTF8_MOUSE),
|
||||
x,
|
||||
y,
|
||||
) {
|
||||
self.input_no_scroll(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn scroll_mouse(
|
||||
&mut self,
|
||||
delta: ScrollDelta,
|
||||
modifiers: &cosmic::iced::keyboard::Modifiers,
|
||||
x: u32,
|
||||
y: u32,
|
||||
) {
|
||||
let term_lock = self.term.lock();
|
||||
let mode = term_lock.mode();
|
||||
if mode.contains(TermMode::SGR_MOUSE) {
|
||||
self.mouse_reporter.report_sgr_mouse_wheel_scroll(
|
||||
self,
|
||||
self.size().cell_width,
|
||||
self.size().cell_height,
|
||||
delta,
|
||||
modifiers,
|
||||
x,
|
||||
y,
|
||||
);
|
||||
} else {
|
||||
self.mouse_reporter.report_mouse_wheel_as_arrows(
|
||||
self,
|
||||
self.size().cell_width,
|
||||
self.size().cell_height,
|
||||
delta,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Terminal {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ pub struct TerminalBox<'a, Message> {
|
|||
click_timing: Duration,
|
||||
context_menu: Option<Point>,
|
||||
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||
mouse_inside_boundary: Option<bool>,
|
||||
on_middle_click: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +64,8 @@ where
|
|||
click_timing: Duration::from_millis(500),
|
||||
context_menu: None,
|
||||
on_context_menu: None,
|
||||
on_mouse_enter: None,
|
||||
mouse_inside_boundary: None,
|
||||
on_middle_click: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +98,11 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn on_mouse_enter(mut self, on_mouse_enter: impl Fn() -> Message + 'a) -> Self {
|
||||
self.on_mouse_enter = Some(Box::new(on_mouse_enter));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_middle_click(mut self, on_middle_click: impl Fn() -> Message + 'a) -> Self {
|
||||
self.on_middle_click = Some(Box::new(on_middle_click));
|
||||
self
|
||||
|
|
@ -592,6 +601,7 @@ where
|
|||
let buffer_size = terminal.with_buffer(|buffer| buffer.size());
|
||||
|
||||
let is_app_cursor = terminal.term.lock().mode().contains(TermMode::APP_CURSOR);
|
||||
let is_mouse_mode = terminal.term.lock().mode().intersects(TermMode::MOUSE_MODE);
|
||||
|
||||
let mut status = Status::Ignored;
|
||||
match event {
|
||||
|
|
@ -632,6 +642,10 @@ where
|
|||
terminal.input_scroll(b"\x1B[1;5H".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Insert => {
|
||||
terminal.input_scroll(b"\x1B[2;5~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
terminal.input_scroll(b"\x1B[3;5~".as_slice());
|
||||
status = Status::Captured;
|
||||
|
|
@ -694,10 +708,102 @@ where
|
|||
}
|
||||
_ => (),
|
||||
},
|
||||
(_, _, true, _) => {
|
||||
// Ignore alt keys
|
||||
//TODO: alt keys for control characters
|
||||
}
|
||||
// Handle alt keys
|
||||
(_, _, true, _) => match key_code {
|
||||
KeyCode::Up => {
|
||||
terminal.input_scroll(b"\x1B[1;3A".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Down => {
|
||||
terminal.input_scroll(b"\x1B[1;3B".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Right => {
|
||||
terminal.input_scroll(b"\x1B[1;3C".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Left => {
|
||||
terminal.input_scroll(b"\x1B[1;3D".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::End => {
|
||||
terminal.input_scroll(b"\x1B[1;3F".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Home => {
|
||||
terminal.input_scroll(b"\x1B[1;3H".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Insert => {
|
||||
terminal.input_scroll(b"\x1B[2;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
terminal.input_scroll(b"\x1B[3;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::PageUp => {
|
||||
terminal.input_scroll(b"\x1B[5;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::PageDown => {
|
||||
terminal.input_scroll(b"\x1B[6;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F1 => {
|
||||
terminal.input_scroll(b"\x1B[1;3P".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F2 => {
|
||||
terminal.input_scroll(b"\x1B1;3Q".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F3 => {
|
||||
terminal.input_scroll(b"\x1B1;3R".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F4 => {
|
||||
terminal.input_scroll(b"\x1B1;3S".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F5 => {
|
||||
terminal.input_scroll(b"\x1B[15;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F6 => {
|
||||
terminal.input_scroll(b"\x1B[17;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F7 => {
|
||||
terminal.input_scroll(b"\x1B[18;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F8 => {
|
||||
terminal.input_scroll(b"\x1B[19;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F9 => {
|
||||
terminal.input_scroll(b"\x1B[20;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F10 => {
|
||||
terminal.input_scroll(b"\x1B[21;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F11 => {
|
||||
terminal.input_scroll(b"\x1B[23;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::F12 => {
|
||||
terminal.input_scroll(b"\x1B[24;3~".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
terminal.input_scroll(b"\x1B\x7F".as_slice());
|
||||
status = Status::Captured;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
// Handle shift keys
|
||||
(_, _, _, true) => match key_code {
|
||||
KeyCode::End => {
|
||||
|
|
@ -911,113 +1017,36 @@ where
|
|||
}
|
||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
state.is_focused = true;
|
||||
let x = p.x - self.padding.left;
|
||||
let y = p.y - self.padding.top;
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
|
||||
// Handle left click drag
|
||||
if let Button::Left = button {
|
||||
let x = p.x - self.padding.left;
|
||||
let y = p.y - self.padding.top;
|
||||
if x >= 0.0 && x < buffer_size.0 && y >= 0.0 && y < buffer_size.1 {
|
||||
let click_kind =
|
||||
if let Some((click_kind, click_time)) = state.click.take() {
|
||||
if click_time.elapsed() < self.click_timing {
|
||||
match click_kind {
|
||||
ClickKind::Single => ClickKind::Double,
|
||||
ClickKind::Double => ClickKind::Triple,
|
||||
ClickKind::Triple => ClickKind::Single,
|
||||
if is_mouse_mode {
|
||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||
} else {
|
||||
state.is_focused = true;
|
||||
|
||||
// Handle left click drag
|
||||
if let Button::Left = button {
|
||||
let x = p.x - self.padding.left;
|
||||
let y = p.y - self.padding.top;
|
||||
if x >= 0.0 && x < buffer_size.0 && y >= 0.0 && y < buffer_size.1 {
|
||||
let click_kind =
|
||||
if let Some((click_kind, click_time)) = state.click.take() {
|
||||
if click_time.elapsed() < self.click_timing {
|
||||
match click_kind {
|
||||
ClickKind::Single => ClickKind::Double,
|
||||
ClickKind::Double => ClickKind::Triple,
|
||||
ClickKind::Triple => ClickKind::Single,
|
||||
}
|
||||
} else {
|
||||
ClickKind::Single
|
||||
}
|
||||
} else {
|
||||
ClickKind::Single
|
||||
}
|
||||
} else {
|
||||
ClickKind::Single
|
||||
};
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
let location = terminal.viewport_to_point(TermPoint::new(
|
||||
row as usize,
|
||||
TermColumn(col as usize),
|
||||
));
|
||||
let side = if col.fract() < 0.5 {
|
||||
TermSide::Left
|
||||
} else {
|
||||
TermSide::Right
|
||||
};
|
||||
let selection = match click_kind {
|
||||
ClickKind::Single => {
|
||||
Selection::new(SelectionType::Simple, location, side)
|
||||
}
|
||||
ClickKind::Double => {
|
||||
Selection::new(SelectionType::Semantic, location, side)
|
||||
}
|
||||
ClickKind::Triple => {
|
||||
Selection::new(SelectionType::Lines, location, side)
|
||||
}
|
||||
};
|
||||
{
|
||||
let mut term = terminal.term.lock();
|
||||
term.selection = Some(selection);
|
||||
}
|
||||
terminal.needs_update = true;
|
||||
state.click = Some((click_kind, Instant::now()));
|
||||
state.dragging = Some(Dragging::Buffer);
|
||||
} else if scrollbar_rect.contains(Point::new(x, y)) {
|
||||
if let Some(start_scroll) = terminal.scrollbar() {
|
||||
state.dragging = Some(Dragging::Scrollbar {
|
||||
start_y: y,
|
||||
start_scroll,
|
||||
});
|
||||
}
|
||||
} else if x >= scrollbar_rect.x
|
||||
&& x < (scrollbar_rect.x + scrollbar_rect.width)
|
||||
{
|
||||
if terminal.scrollbar().is_some() {
|
||||
let scroll_ratio =
|
||||
terminal.with_buffer(|buffer| y / buffer.size().1);
|
||||
terminal.scroll_to(scroll_ratio);
|
||||
if let Some(start_scroll) = terminal.scrollbar() {
|
||||
state.dragging = Some(Dragging::Scrollbar {
|
||||
start_y: y,
|
||||
start_scroll,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if button == Button::Middle {
|
||||
if let Some(on_middle_click) = &self.on_middle_click {
|
||||
shell.publish(on_middle_click());
|
||||
}
|
||||
}
|
||||
|
||||
// Update context menu state
|
||||
if let Some(on_context_menu) = &self.on_context_menu {
|
||||
shell.publish((on_context_menu)(match self.context_menu {
|
||||
Some(_) => None,
|
||||
None => match button {
|
||||
Button::Right => Some(p),
|
||||
_ => None,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
|
||||
state.dragging = None;
|
||||
status = Status::Captured;
|
||||
}
|
||||
Event::Mouse(MouseEvent::CursorMoved { .. }) => {
|
||||
if let Some(dragging) = &state.dragging {
|
||||
if let Some(p) = cursor_position.position() {
|
||||
let x = (p.x - layout.bounds().x) - self.padding.left;
|
||||
let y = (p.y - layout.bounds().y) - self.padding.top;
|
||||
match dragging {
|
||||
Dragging::Buffer => {
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
};
|
||||
let location = terminal.viewport_to_point(TermPoint::new(
|
||||
row as usize,
|
||||
TermColumn(col as usize),
|
||||
|
|
@ -1027,56 +1056,189 @@ where
|
|||
} else {
|
||||
TermSide::Right
|
||||
};
|
||||
let selection = match click_kind {
|
||||
ClickKind::Single => {
|
||||
Selection::new(SelectionType::Simple, location, side)
|
||||
}
|
||||
ClickKind::Double => {
|
||||
Selection::new(SelectionType::Semantic, location, side)
|
||||
}
|
||||
ClickKind::Triple => {
|
||||
Selection::new(SelectionType::Lines, location, side)
|
||||
}
|
||||
};
|
||||
{
|
||||
let mut term = terminal.term.lock();
|
||||
if let Some(selection) = &mut term.selection {
|
||||
selection.update(location, side);
|
||||
}
|
||||
term.selection = Some(selection);
|
||||
}
|
||||
terminal.needs_update = true;
|
||||
state.click = Some((click_kind, Instant::now()));
|
||||
state.dragging = Some(Dragging::Buffer);
|
||||
} else if scrollbar_rect.contains(Point::new(x, y)) {
|
||||
if let Some(start_scroll) = terminal.scrollbar() {
|
||||
state.dragging = Some(Dragging::Scrollbar {
|
||||
start_y: y,
|
||||
start_scroll,
|
||||
});
|
||||
}
|
||||
} else if x >= scrollbar_rect.x
|
||||
&& x < (scrollbar_rect.x + scrollbar_rect.width)
|
||||
{
|
||||
if terminal.scrollbar().is_some() {
|
||||
let scroll_ratio =
|
||||
terminal.with_buffer(|buffer| y / buffer.size().1);
|
||||
terminal.scroll_to(scroll_ratio);
|
||||
if let Some(start_scroll) = terminal.scrollbar() {
|
||||
state.dragging = Some(Dragging::Scrollbar {
|
||||
start_y: y,
|
||||
start_scroll,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Dragging::Scrollbar {
|
||||
start_y,
|
||||
start_scroll,
|
||||
} => {
|
||||
let scroll_offset = terminal
|
||||
.with_buffer(|buffer| ((y - start_y) / buffer.size().1));
|
||||
terminal.scroll_to(start_scroll.0 + scroll_offset);
|
||||
} else if button == Button::Middle {
|
||||
if let Some(on_middle_click) = &self.on_middle_click {
|
||||
shell.publish(on_middle_click());
|
||||
}
|
||||
}
|
||||
// Update context menu state
|
||||
if let Some(on_context_menu) = &self.on_context_menu {
|
||||
shell.publish((on_context_menu)(match self.context_menu {
|
||||
Some(_) => None,
|
||||
None => match button {
|
||||
Button::Right => Some(p),
|
||||
_ => None,
|
||||
},
|
||||
}));
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
|
||||
state.dragging = None;
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
let x = p.x - self.padding.left;
|
||||
let y = p.y - self.padding.top;
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
if is_mouse_mode {
|
||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||
} else {
|
||||
status = Status::Captured;
|
||||
}
|
||||
} else {
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::WheelScrolled { delta }) => {
|
||||
if let Some(_p) = cursor_position.position_in(layout.bounds()) {
|
||||
match delta {
|
||||
ScrollDelta::Lines { x: _, y } => {
|
||||
//TODO: this adjustment is just a guess!
|
||||
state.scroll_pixels = 0.0;
|
||||
let lines = (-y * 6.0) as i32;
|
||||
if lines != 0 {
|
||||
terminal.scroll(TerminalScroll::Delta(-lines));
|
||||
Event::Mouse(MouseEvent::ButtonReleased(_button)) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
let x = p.x - self.padding.left;
|
||||
let y = p.y - self.padding.top;
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
if is_mouse_mode {
|
||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::CursorMoved { .. }) => {
|
||||
if let Some(on_mouse_enter) = &self.on_mouse_enter {
|
||||
let mouse_is_inside = cursor_position.position_in(layout.bounds()).is_some();
|
||||
if let Some(known_is_inside) = self.mouse_inside_boundary {
|
||||
if mouse_is_inside != known_is_inside {
|
||||
if mouse_is_inside {
|
||||
shell.publish(on_mouse_enter());
|
||||
}
|
||||
status = Status::Captured;
|
||||
self.mouse_inside_boundary = Some(mouse_is_inside);
|
||||
}
|
||||
ScrollDelta::Pixels { x: _, y } => {
|
||||
//TODO: this adjustment is just a guess!
|
||||
state.scroll_pixels -= y * 6.0;
|
||||
let mut lines = 0;
|
||||
let metrics = terminal.with_buffer(|buffer| buffer.metrics());
|
||||
while state.scroll_pixels <= -metrics.line_height {
|
||||
lines -= 1;
|
||||
state.scroll_pixels += metrics.line_height;
|
||||
} else {
|
||||
self.mouse_inside_boundary = Some(mouse_is_inside);
|
||||
}
|
||||
}
|
||||
if let Some(p) = cursor_position.position() {
|
||||
let x = (p.x - layout.bounds().x) - self.padding.left;
|
||||
let y = (p.y - layout.bounds().y) - self.padding.top;
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
if is_mouse_mode {
|
||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||
} else {
|
||||
if let Some(dragging) = &state.dragging {
|
||||
match dragging {
|
||||
Dragging::Buffer => {
|
||||
let location = terminal.viewport_to_point(TermPoint::new(
|
||||
row as usize,
|
||||
TermColumn(col as usize),
|
||||
));
|
||||
let side = if col.fract() < 0.5 {
|
||||
TermSide::Left
|
||||
} else {
|
||||
TermSide::Right
|
||||
};
|
||||
{
|
||||
let mut term = terminal.term.lock();
|
||||
if let Some(selection) = &mut term.selection {
|
||||
selection.update(location, side);
|
||||
}
|
||||
}
|
||||
terminal.needs_update = true;
|
||||
}
|
||||
Dragging::Scrollbar {
|
||||
start_y,
|
||||
start_scroll,
|
||||
} => {
|
||||
let scroll_offset = terminal
|
||||
.with_buffer(|buffer| ((y - start_y) / buffer.size().1));
|
||||
terminal.scroll_to(start_scroll.0 + scroll_offset);
|
||||
}
|
||||
}
|
||||
while state.scroll_pixels >= metrics.line_height {
|
||||
lines += 1;
|
||||
state.scroll_pixels -= metrics.line_height;
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::WheelScrolled { delta }) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
if is_mouse_mode {
|
||||
let x = (p.x - layout.bounds().x) - self.padding.left;
|
||||
let y = (p.y - layout.bounds().y) - self.padding.top;
|
||||
//TODO: better calculation of position
|
||||
let col = x / terminal.size().cell_width;
|
||||
let row = y / terminal.size().cell_height;
|
||||
terminal.scroll_mouse(delta, &state.modifiers, col as u32, row as u32);
|
||||
} else {
|
||||
match delta {
|
||||
ScrollDelta::Lines { x: _, y } => {
|
||||
//TODO: this adjustment is just a guess!
|
||||
state.scroll_pixels = 0.0;
|
||||
let lines = (-y * 6.0) as i32;
|
||||
if lines != 0 {
|
||||
terminal.scroll(TerminalScroll::Delta(-lines));
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
if lines != 0 {
|
||||
terminal.scroll(TerminalScroll::Delta(-lines));
|
||||
ScrollDelta::Pixels { x: _, y } => {
|
||||
//TODO: this adjustment is just a guess!
|
||||
state.scroll_pixels -= y * 6.0;
|
||||
let mut lines = 0;
|
||||
let metrics = terminal.with_buffer(|buffer| buffer.metrics());
|
||||
while state.scroll_pixels <= -metrics.line_height {
|
||||
lines -= 1;
|
||||
state.scroll_pixels += metrics.line_height;
|
||||
}
|
||||
while state.scroll_pixels >= metrics.line_height {
|
||||
lines += 1;
|
||||
state.scroll_pixels -= metrics.line_height;
|
||||
}
|
||||
if lines != 0 {
|
||||
terminal.scroll(TerminalScroll::Delta(-lines));
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,313 @@ fn auto_colors() -> Colors {
|
|||
colors
|
||||
}
|
||||
|
||||
fn tango_palette() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
let encode_rgb = |data: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (data >> 16) as u8,
|
||||
g: (data >> 8) as u8,
|
||||
b: data as u8,
|
||||
}
|
||||
};
|
||||
|
||||
colors[NamedColor::Black] = Some(encode_rgb(0x2E3436));
|
||||
colors[NamedColor::Red] = Some(encode_rgb(0xCC0000));
|
||||
colors[NamedColor::Green] = Some(encode_rgb(0x4E9A06));
|
||||
colors[NamedColor::Yellow] = Some(encode_rgb(0xC4A000));
|
||||
colors[NamedColor::Blue] = Some(encode_rgb(0x3465A4));
|
||||
colors[NamedColor::Magenta] = Some(encode_rgb(0x75507B));
|
||||
colors[NamedColor::Cyan] = Some(encode_rgb(0x06989A));
|
||||
colors[NamedColor::White] = Some(encode_rgb(0xD3D7CF));
|
||||
|
||||
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x555753));
|
||||
colors[NamedColor::BrightRed] = Some(encode_rgb(0xEF2929));
|
||||
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x8AE234));
|
||||
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xFCE94F));
|
||||
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x729FCF));
|
||||
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xAD7FA8));
|
||||
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x34E2E2));
|
||||
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xEEEEEC));
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn tango_dark() -> Colors {
|
||||
let mut colors = tango_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::White];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new()
|
||||
// Dim less so colors are readable with default bg
|
||||
.with_dim_lightness_adjustment(-0.10)
|
||||
.fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn tango_light() -> Colors {
|
||||
let mut colors = tango_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::Black];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightBlack];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn linux_console_palette() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
let encode_rgb = |data: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (data >> 16) as u8,
|
||||
g: (data >> 8) as u8,
|
||||
b: data as u8,
|
||||
}
|
||||
};
|
||||
|
||||
colors[NamedColor::Black] = Some(encode_rgb(0x000000));
|
||||
colors[NamedColor::Red] = Some(encode_rgb(0xAA0000));
|
||||
colors[NamedColor::Green] = Some(encode_rgb(0x00AA00));
|
||||
colors[NamedColor::Yellow] = Some(encode_rgb(0xAA5500));
|
||||
colors[NamedColor::Blue] = Some(encode_rgb(0x0000AA));
|
||||
colors[NamedColor::Magenta] = Some(encode_rgb(0xAA00AA));
|
||||
colors[NamedColor::Cyan] = Some(encode_rgb(0x00AAAA));
|
||||
colors[NamedColor::White] = Some(encode_rgb(0xAAAAAA));
|
||||
|
||||
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x555555));
|
||||
colors[NamedColor::BrightRed] = Some(encode_rgb(0xFF5555));
|
||||
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x55FF55));
|
||||
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xFFFF55));
|
||||
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x5555FF));
|
||||
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xFF55FF));
|
||||
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x55FFFF));
|
||||
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xFFFFFF));
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn linux_console() -> Colors {
|
||||
let mut colors = linux_console_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::White];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new()
|
||||
// Dim less so colors are readable with default bg
|
||||
.with_dim_lightness_adjustment(-0.10)
|
||||
.fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn xterm_palette() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
let encode_rgb = |data: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (data >> 16) as u8,
|
||||
g: (data >> 8) as u8,
|
||||
b: data as u8,
|
||||
}
|
||||
};
|
||||
|
||||
colors[NamedColor::Black] = Some(encode_rgb(0x000000));
|
||||
colors[NamedColor::Red] = Some(encode_rgb(0xCD0000));
|
||||
colors[NamedColor::Green] = Some(encode_rgb(0x00CD00));
|
||||
colors[NamedColor::Yellow] = Some(encode_rgb(0xCDCD00));
|
||||
colors[NamedColor::Blue] = Some(encode_rgb(0x0000EE));
|
||||
colors[NamedColor::Magenta] = Some(encode_rgb(0xCD00CD));
|
||||
colors[NamedColor::Cyan] = Some(encode_rgb(0x00CDCD));
|
||||
colors[NamedColor::White] = Some(encode_rgb(0xE5E5E5));
|
||||
|
||||
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x7F7F7F));
|
||||
colors[NamedColor::BrightRed] = Some(encode_rgb(0xFF0000));
|
||||
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x00FF00));
|
||||
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xFFFF00));
|
||||
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x5C5CFF));
|
||||
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xFF00FF));
|
||||
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x00FFFF));
|
||||
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xFFFFFF));
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn xterm_dark() -> Colors {
|
||||
let mut colors = xterm_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::White];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new()
|
||||
// Dim less so colors are readable with default bg
|
||||
.with_dim_lightness_adjustment(-0.12)
|
||||
.fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn xterm_light() -> Colors {
|
||||
let mut colors = xterm_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::Black];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightBlack];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn rxvt_palette() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
let encode_rgb = |data: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (data >> 16) as u8,
|
||||
g: (data >> 8) as u8,
|
||||
b: data as u8,
|
||||
}
|
||||
};
|
||||
|
||||
colors[NamedColor::Black] = Some(encode_rgb(0x000000));
|
||||
colors[NamedColor::Red] = Some(encode_rgb(0xCD0000));
|
||||
colors[NamedColor::Green] = Some(encode_rgb(0x00CD00));
|
||||
colors[NamedColor::Yellow] = Some(encode_rgb(0xCDCD00));
|
||||
colors[NamedColor::Blue] = Some(encode_rgb(0x0000CD));
|
||||
colors[NamedColor::Magenta] = Some(encode_rgb(0xCD00CD));
|
||||
colors[NamedColor::Cyan] = Some(encode_rgb(0x00CDCD));
|
||||
colors[NamedColor::White] = Some(encode_rgb(0xFAEBD7));
|
||||
|
||||
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x404040));
|
||||
colors[NamedColor::BrightRed] = Some(encode_rgb(0xFF0000));
|
||||
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x00FF00));
|
||||
colors[NamedColor::BrightYellow] = Some(encode_rgb(0xFFFF00));
|
||||
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x0000FF));
|
||||
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0xFF00FF));
|
||||
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x00FFFF));
|
||||
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xFFFFFF));
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn rxvt_dark() -> Colors {
|
||||
let mut colors = rxvt_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::White];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new()
|
||||
// Dim less so colors are readable with default bg
|
||||
.with_dim_lightness_adjustment(-0.12)
|
||||
.fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn rxvt_light() -> Colors {
|
||||
let mut colors = rxvt_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::Black];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightBlack];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn solarized_palette() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
let encode_rgb = |data: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (data >> 16) as u8,
|
||||
g: (data >> 8) as u8,
|
||||
b: data as u8,
|
||||
}
|
||||
};
|
||||
|
||||
colors[NamedColor::Black] = Some(encode_rgb(0x073642));
|
||||
colors[NamedColor::Red] = Some(encode_rgb(0xDC322F));
|
||||
colors[NamedColor::Green] = Some(encode_rgb(0x859900));
|
||||
colors[NamedColor::Yellow] = Some(encode_rgb(0xB58900));
|
||||
colors[NamedColor::Blue] = Some(encode_rgb(0x268BD2));
|
||||
colors[NamedColor::Magenta] = Some(encode_rgb(0xD33682));
|
||||
colors[NamedColor::Cyan] = Some(encode_rgb(0x2AA198));
|
||||
colors[NamedColor::White] = Some(encode_rgb(0xEEE8D5));
|
||||
|
||||
colors[NamedColor::BrightBlack] = Some(encode_rgb(0x002B36));
|
||||
colors[NamedColor::BrightRed] = Some(encode_rgb(0xCB4B16));
|
||||
colors[NamedColor::BrightGreen] = Some(encode_rgb(0x586E75));
|
||||
colors[NamedColor::BrightYellow] = Some(encode_rgb(0x657B83));
|
||||
colors[NamedColor::BrightBlue] = Some(encode_rgb(0x839496));
|
||||
colors[NamedColor::BrightMagenta] = Some(encode_rgb(0x6C71C4));
|
||||
colors[NamedColor::BrightCyan] = Some(encode_rgb(0x93A1A1));
|
||||
colors[NamedColor::BrightWhite] = Some(encode_rgb(0xFDF6E3));
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn solarized_dark() -> Colors {
|
||||
let mut colors = solarized_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightBlue];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightBlack];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::Blue];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn solarized_light() -> Colors {
|
||||
let mut colors = solarized_palette();
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightYellow];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::Yellow];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Foreground];
|
||||
|
||||
// Fill missing dim colors
|
||||
ColorDerive::new().fill_missing_dims(&mut colors);
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn cosmic_dark() -> Colors {
|
||||
let mut colors = auto_colors();
|
||||
|
||||
|
|
@ -169,7 +476,7 @@ fn cosmic_dark() -> Colors {
|
|||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
// Background comes from theme settings: colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite];
|
||||
|
||||
|
|
@ -210,7 +517,7 @@ fn cosmic_light() -> Colors {
|
|||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::Black];
|
||||
colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
// Background comes from theme settings: colors[NamedColor::Background] = colors[NamedColor::BrightWhite];
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::Black];
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightBlack];
|
||||
|
||||
|
|
@ -351,6 +658,15 @@ fn pop_dark() -> Colors {
|
|||
|
||||
pub fn terminal_themes() -> HashMap<String, Colors> {
|
||||
let mut themes = HashMap::new();
|
||||
themes.insert("Tango Dark".to_string(), tango_dark());
|
||||
themes.insert("Tango Light".to_string(), tango_light());
|
||||
themes.insert("XTerm Dark".to_string(), xterm_dark());
|
||||
themes.insert("XTerm Light".to_string(), xterm_light());
|
||||
themes.insert("Linux Console".to_string(), linux_console());
|
||||
themes.insert("Rxvt Dark".to_string(), rxvt_dark());
|
||||
themes.insert("Rxvt Light".to_string(), rxvt_light());
|
||||
themes.insert("Solarized Dark".to_string(), solarized_dark());
|
||||
themes.insert("Solarized Light".to_string(), solarized_light());
|
||||
themes.insert("COSMIC Dark".to_string(), cosmic_dark());
|
||||
themes.insert("COSMIC Light".to_string(), cosmic_light());
|
||||
themes.insert("gruvbox-dark".to_string(), gruvbox_dark());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue