(feat): Text zoom for cosmic-edit.
This commit is contained in:
parent
48011f1847
commit
5b71f5f850
6 changed files with 123 additions and 5 deletions
|
|
@ -47,6 +47,8 @@ syntax-dark = Syntax dark
|
||||||
syntax-light = Syntax light
|
syntax-light = Syntax light
|
||||||
default-font = Default font
|
default-font = Default font
|
||||||
default-font-size = Default font size
|
default-font-size = Default font size
|
||||||
|
default-zoom-step = Zoom steps
|
||||||
|
|
||||||
|
|
||||||
### Keyboard shortcuts
|
### Keyboard shortcuts
|
||||||
keyboard-shortcuts = Keyboard shortcuts
|
keyboard-shortcuts = Keyboard shortcuts
|
||||||
|
|
@ -99,6 +101,9 @@ spell-check = Spell check...
|
||||||
|
|
||||||
## View
|
## View
|
||||||
view = View
|
view = View
|
||||||
|
zoom-in = Zoom in
|
||||||
|
default-size = Default size
|
||||||
|
zoom-out = Zoom out
|
||||||
indentation = Indentation
|
indentation = Indentation
|
||||||
|
|
||||||
### Indentation
|
### Indentation
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ pub struct Config {
|
||||||
pub find_wrap_around: bool,
|
pub find_wrap_around: bool,
|
||||||
pub font_name: String,
|
pub font_name: String,
|
||||||
pub font_size: u16,
|
pub font_size: u16,
|
||||||
|
pub font_size_zoom_step_mul_100: u16,
|
||||||
pub highlight_current_line: bool,
|
pub highlight_current_line: bool,
|
||||||
pub line_numbers: bool,
|
pub line_numbers: bool,
|
||||||
pub syntax_theme_dark: String,
|
pub syntax_theme_dark: String,
|
||||||
|
|
@ -63,6 +64,7 @@ impl Default for Config {
|
||||||
find_wrap_around: true,
|
find_wrap_around: true,
|
||||||
font_name: "Fira Mono".to_string(),
|
font_name: "Fira Mono".to_string(),
|
||||||
font_size: 14,
|
font_size: 14,
|
||||||
|
font_size_zoom_step_mul_100: 100,
|
||||||
highlight_current_line: true,
|
highlight_current_line: true,
|
||||||
line_numbers: true,
|
line_numbers: true,
|
||||||
syntax_theme_dark: "COSMIC Dark".to_string(),
|
syntax_theme_dark: "COSMIC Dark".to_string(),
|
||||||
|
|
@ -75,6 +77,13 @@ impl Default for Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
pub fn font_size_adjusted(&self, zoom_adj: i8) -> f32 {
|
||||||
|
let font_size = f32::from(self.font_size).max(1.0);
|
||||||
|
let adj = f32::from(zoom_adj);
|
||||||
|
let adj_step = f32::from(self.font_size_zoom_step_mul_100) / 100.0;
|
||||||
|
(font_size + adj * adj_step).max(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_regex(&self, pattern: &str) -> Result<regex::Regex, regex::Error> {
|
pub fn find_regex(&self, pattern: &str) -> Result<regex::Regex, regex::Error> {
|
||||||
let mut builder = if self.find_use_regex {
|
let mut builder = if self.find_use_regex {
|
||||||
regex::RegexBuilder::new(pattern)
|
regex::RegexBuilder::new(pattern)
|
||||||
|
|
@ -86,8 +95,8 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate metrics from font size
|
// Calculate metrics from font size
|
||||||
pub fn metrics(&self) -> Metrics {
|
pub fn metrics(&self, zoom_adj: i8) -> Metrics {
|
||||||
let font_size = self.font_size.max(1) as f32;
|
let font_size = self.font_size_adjusted(zoom_adj);
|
||||||
let line_height = (font_size * 1.4).ceil();
|
let line_height = (font_size * 1.4).ceil();
|
||||||
Metrics::new(font_size, line_height)
|
Metrics::new(font_size, line_height)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
|
||||||
bind!([Ctrl], Key::Character("s".into()), Save);
|
bind!([Ctrl], Key::Character("s".into()), Save);
|
||||||
bind!([Ctrl, Shift], Key::Character("S".into()), SaveAsDialog);
|
bind!([Ctrl, Shift], Key::Character("S".into()), SaveAsDialog);
|
||||||
bind!([Ctrl], Key::Character("a".into()), SelectAll);
|
bind!([Ctrl], Key::Character("a".into()), SelectAll);
|
||||||
|
// Ctrl+0, Ctrl+-, and Ctrl+= are not special keys for terminals and are free to use
|
||||||
|
bind!([Ctrl], Key::Character("0".into()), ZoomReset);
|
||||||
|
bind!([Ctrl], Key::Character("-".into()), ZoomOut);
|
||||||
|
bind!([Ctrl], Key::Character("=".into()), ZoomIn);
|
||||||
|
bind!([Ctrl], Key::Character("+".into()), ZoomIn);
|
||||||
|
|
||||||
bind!([Ctrl], Key::Character("1".into()), TabActivate0);
|
bind!([Ctrl], Key::Character("1".into()), TabActivate0);
|
||||||
bind!([Ctrl], Key::Character("2".into()), TabActivate1);
|
bind!([Ctrl], Key::Character("2".into()), TabActivate1);
|
||||||
bind!([Ctrl], Key::Character("3".into()), TabActivate2);
|
bind!([Ctrl], Key::Character("3".into()), TabActivate2);
|
||||||
|
|
|
||||||
86
src/main.rs
86
src/main.rs
|
|
@ -225,6 +225,9 @@ pub enum Action {
|
||||||
ToggleSettingsPage,
|
ToggleSettingsPage,
|
||||||
ToggleWordWrap,
|
ToggleWordWrap,
|
||||||
Undo,
|
Undo,
|
||||||
|
ZoomIn,
|
||||||
|
ZoomOut,
|
||||||
|
ZoomReset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
|
|
@ -274,6 +277,9 @@ impl Action {
|
||||||
Self::ToggleSettingsPage => Message::ToggleContextPage(ContextPage::Settings),
|
Self::ToggleSettingsPage => Message::ToggleContextPage(ContextPage::Settings),
|
||||||
Self::ToggleWordWrap => Message::ToggleWordWrap,
|
Self::ToggleWordWrap => Message::ToggleWordWrap,
|
||||||
Self::Undo => Message::Undo,
|
Self::Undo => Message::Undo,
|
||||||
|
Self::ZoomIn => Message::ZoomIn,
|
||||||
|
Self::ZoomOut => Message::ZoomOut,
|
||||||
|
Self::ZoomReset => Message::ZoomReset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,6 +335,10 @@ pub enum Message {
|
||||||
Cut,
|
Cut,
|
||||||
DefaultFont(usize),
|
DefaultFont(usize),
|
||||||
DefaultFontSize(usize),
|
DefaultFontSize(usize),
|
||||||
|
ZoomIn,
|
||||||
|
ZoomOut,
|
||||||
|
ZoomReset,
|
||||||
|
DefaultZoomStep(usize),
|
||||||
DialogCancel,
|
DialogCancel,
|
||||||
DialogMessage(DialogMessage),
|
DialogMessage(DialogMessage),
|
||||||
Find(Option<bool>),
|
Find(Option<bool>),
|
||||||
|
|
@ -433,6 +443,8 @@ pub struct App {
|
||||||
config: Config,
|
config: Config,
|
||||||
config_state_handler: Option<cosmic_config::Config>,
|
config_state_handler: Option<cosmic_config::Config>,
|
||||||
config_state: ConfigState,
|
config_state: ConfigState,
|
||||||
|
zoom_step_names: Vec<String>,
|
||||||
|
zoom_steps: Vec<u16>,
|
||||||
key_binds: HashMap<KeyBind, Action>,
|
key_binds: HashMap<KeyBind, Action>,
|
||||||
app_themes: Vec<String>,
|
app_themes: Vec<String>,
|
||||||
font_names: Vec<String>,
|
font_names: Vec<String>,
|
||||||
|
|
@ -684,6 +696,36 @@ impl App {
|
||||||
self.update_config()
|
self.update_config()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_render_active_tab_zoom(&mut self, zoom_message: Message) -> Task<Message> {
|
||||||
|
if let Some(Tab::Editor(tab)) = self.active_tab_mut() {
|
||||||
|
let current_zoom_adj = tab.zoom_adj();
|
||||||
|
match zoom_message {
|
||||||
|
Message::ZoomIn => tab.set_zoom_adj(current_zoom_adj.saturating_add(1)),
|
||||||
|
Message::ZoomOut => tab.set_zoom_adj(current_zoom_adj.saturating_sub(1)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let entities: Vec<_> = self.tab_model.iter().collect();
|
||||||
|
for entity in entities {
|
||||||
|
if self.tab_model.is_active(entity) {
|
||||||
|
if let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity) {
|
||||||
|
eprintln!("setting stuff");
|
||||||
|
tab.set_config(&self.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_tabs_zoom(&mut self) {
|
||||||
|
let entities: Vec<_> = self.tab_model.iter().collect();
|
||||||
|
for entity in entities {
|
||||||
|
if let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity) {
|
||||||
|
tab.set_zoom_adj(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn save_config_state(&mut self) {
|
fn save_config_state(&mut self) {
|
||||||
if let Some(ref config_state_handler) = self.config_state_handler {
|
if let Some(ref config_state_handler) = self.config_state_handler {
|
||||||
if let Err(err) = self.config_state.write_entry(config_state_handler) {
|
if let Err(err) = self.config_state.write_entry(config_state_handler) {
|
||||||
|
|
@ -1185,6 +1227,10 @@ impl App {
|
||||||
.font_sizes
|
.font_sizes
|
||||||
.iter()
|
.iter()
|
||||||
.position(|font_size| font_size == &self.config.font_size);
|
.position(|font_size| font_size == &self.config.font_size);
|
||||||
|
let zoom_step_selected = self
|
||||||
|
.zoom_steps
|
||||||
|
.iter()
|
||||||
|
.position(|zoom_step| zoom_step == &self.config.font_size_zoom_step_mul_100);
|
||||||
widget::settings::view_column(vec![
|
widget::settings::view_column(vec![
|
||||||
widget::settings::section()
|
widget::settings::section()
|
||||||
.title(fl!("appearance"))
|
.title(fl!("appearance"))
|
||||||
|
|
@ -1229,6 +1275,13 @@ impl App {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.add(
|
||||||
|
widget::settings::item::builder(fl!("default-zoom-step")).control(
|
||||||
|
widget::dropdown(&self.zoom_step_names, zoom_step_selected, |index| {
|
||||||
|
Message::DefaultZoomStep(index)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
widget::settings::section()
|
widget::settings::section()
|
||||||
.title(fl!("keyboard-shortcuts"))
|
.title(fl!("keyboard-shortcuts"))
|
||||||
|
|
@ -1311,6 +1364,13 @@ impl Application for App {
|
||||||
theme_names.push(theme_name.to_string());
|
theme_names.push(theme_name.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut zoom_step_names = Vec::new();
|
||||||
|
let mut zoom_steps = Vec::new();
|
||||||
|
for zoom_step in [25, 50, 75, 100, 150, 200] {
|
||||||
|
zoom_step_names.push(format!("{}px", f32::from(zoom_step) / 100.0));
|
||||||
|
zoom_steps.push(zoom_step);
|
||||||
|
}
|
||||||
|
|
||||||
let mut app = App {
|
let mut app = App {
|
||||||
core,
|
core,
|
||||||
nav_model: nav_bar::Model::builder().build(),
|
nav_model: nav_bar::Model::builder().build(),
|
||||||
|
|
@ -1320,6 +1380,8 @@ impl Application for App {
|
||||||
config_state_handler: flags.config_state_handler,
|
config_state_handler: flags.config_state_handler,
|
||||||
config_state: flags.config_state,
|
config_state: flags.config_state,
|
||||||
key_binds: key_binds(),
|
key_binds: key_binds(),
|
||||||
|
zoom_step_names,
|
||||||
|
zoom_steps,
|
||||||
app_themes,
|
app_themes,
|
||||||
font_names,
|
font_names,
|
||||||
font_size_names,
|
font_size_names,
|
||||||
|
|
@ -1699,12 +1761,34 @@ impl Application for App {
|
||||||
Message::DefaultFontSize(index) => match self.font_sizes.get(index) {
|
Message::DefaultFontSize(index) => match self.font_sizes.get(index) {
|
||||||
Some(font_size) => {
|
Some(font_size) => {
|
||||||
self.config.font_size = *font_size;
|
self.config.font_size = *font_size;
|
||||||
|
self.reset_tabs_zoom();
|
||||||
return self.save_config();
|
return self.save_config();
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::warn!("failed to find font with index {}", index);
|
log::warn!("failed to find font with index {}", index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Message::ZoomIn => {
|
||||||
|
return self.update_render_active_tab_zoom(message);
|
||||||
|
}
|
||||||
|
Message::ZoomOut => {
|
||||||
|
return self.update_render_active_tab_zoom(message);
|
||||||
|
}
|
||||||
|
Message::ZoomReset => {
|
||||||
|
self.reset_tabs_zoom();
|
||||||
|
return self.save_config();
|
||||||
|
}
|
||||||
|
Message::DefaultZoomStep(index) => match self.zoom_steps.get(index) {
|
||||||
|
Some(zoom_step) => {
|
||||||
|
self.config.font_size_zoom_step_mul_100 = *zoom_step;
|
||||||
|
self.reset_tabs_zoom(); // reset zoom
|
||||||
|
return self.save_config();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::warn!("failed to find zoom step with index {}", index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Message::DialogCancel => {
|
Message::DialogCancel => {
|
||||||
self.dialog_page_opt = None;
|
self.dialog_page_opt = None;
|
||||||
}
|
}
|
||||||
|
|
@ -2703,7 +2787,7 @@ impl Application for App {
|
||||||
let tab_id = self.tab_model.active();
|
let tab_id = self.tab_model.active();
|
||||||
match self.tab_model.data::<Tab>(tab_id) {
|
match self.tab_model.data::<Tab>(tab_id) {
|
||||||
Some(Tab::Editor(tab)) => {
|
Some(Tab::Editor(tab)) => {
|
||||||
let mut text_box = text_box(&tab.editor, self.config.metrics())
|
let mut text_box = text_box(&tab.editor, self.config.metrics(tab.zoom_adj()))
|
||||||
.id(self.text_box_id.clone())
|
.id(self.text_box_id.clone())
|
||||||
.on_auto_scroll(Message::AutoScroll)
|
.on_auto_scroll(Message::AutoScroll)
|
||||||
.on_changed(Message::TabChanged(tab_id))
|
.on_changed(Message::TabChanged(tab_id))
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,10 @@ pub fn menu_bar<'a>(
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
MenuItem::Divider,
|
MenuItem::Divider,
|
||||||
|
MenuItem::Button(fl!("zoom-in"), None, Action::ZoomIn),
|
||||||
|
MenuItem::Button(fl!("default-size"), None, Action::ZoomReset),
|
||||||
|
MenuItem::Button(fl!("zoom-out"), None, Action::ZoomOut),
|
||||||
|
MenuItem::Divider,
|
||||||
MenuItem::CheckBox(
|
MenuItem::CheckBox(
|
||||||
fl!("word-wrap"),
|
fl!("word-wrap"),
|
||||||
None,
|
None,
|
||||||
|
|
|
||||||
14
src/tab.rs
14
src/tab.rs
|
|
@ -42,14 +42,15 @@ pub struct EditorTab {
|
||||||
attrs: Attrs<'static>,
|
attrs: Attrs<'static>,
|
||||||
pub editor: Mutex<ViEditor<'static, 'static>>,
|
pub editor: Mutex<ViEditor<'static, 'static>>,
|
||||||
pub context_menu: Option<Point>,
|
pub context_menu: Option<Point>,
|
||||||
|
pub zoom_adj: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorTab {
|
impl EditorTab {
|
||||||
pub fn new(config: &Config) -> Self {
|
pub fn new(config: &Config) -> Self {
|
||||||
//TODO: do not repeat, used in App::init
|
//TODO: do not repeat, used in App::init
|
||||||
let attrs = Attrs::new().family(cosmic_text::Family::Monospace);
|
let attrs = Attrs::new().family(cosmic_text::Family::Monospace);
|
||||||
|
let zoom_adj = Default::default();
|
||||||
let mut buffer = Buffer::new_empty(config.metrics());
|
let mut buffer = Buffer::new_empty(config.metrics(zoom_adj));
|
||||||
buffer.set_text(
|
buffer.set_text(
|
||||||
font_system().write().unwrap().raw(),
|
font_system().write().unwrap().raw(),
|
||||||
"",
|
"",
|
||||||
|
|
@ -69,6 +70,7 @@ impl EditorTab {
|
||||||
attrs,
|
attrs,
|
||||||
editor: Mutex::new(ViEditor::new(editor)),
|
editor: Mutex::new(ViEditor::new(editor)),
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
|
zoom_adj,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update any other config settings
|
// Update any other config settings
|
||||||
|
|
@ -309,6 +311,14 @@ impl EditorTab {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zoom_adj(&self) -> i8 {
|
||||||
|
self.zoom_adj
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_zoom_adj(&mut self, value: i8) {
|
||||||
|
self.zoom_adj = value;
|
||||||
|
}
|
||||||
|
|
||||||
// Code adapted from cosmic-text ViEditor search
|
// Code adapted from cosmic-text ViEditor search
|
||||||
pub fn search(&self, regex: &Regex, forwards: bool, wrap_around: bool) -> bool {
|
pub fn search(&self, regex: &Regex, forwards: bool, wrap_around: bool) -> bool {
|
||||||
let mut editor = self.editor.lock().unwrap();
|
let mut editor = self.editor.lock().unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue