Add find stub
This commit is contained in:
parent
8766819ea8
commit
928269a239
9 changed files with 266 additions and 17 deletions
|
|
@ -20,6 +20,11 @@ use-bright-bold = Use bright colors with bold text
|
||||||
default-font-size = Default font size
|
default-font-size = Default font size
|
||||||
default-zoom-step = Default zoom step
|
default-zoom-step = Default zoom step
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find-placeholder = Find...
|
||||||
|
find-previous = Find previous
|
||||||
|
find-next = Find next
|
||||||
|
|
||||||
# Menu
|
# Menu
|
||||||
|
|
||||||
## File
|
## File
|
||||||
|
|
|
||||||
3
res/icons/edit-clear-symbolic.svg
Normal file
3
res/icons/edit-clear-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 2.00305L0 8.00305L5 14.0031H16V2.00305H5ZM7 5.00305H8C8.277 5.00305 8.526 5.11505 8.707 5.29605L10 6.58905L11.293 5.29605C11.3856 5.20288 11.4958 5.129 11.6171 5.0787C11.7385 5.02841 11.8686 5.0027 12 5.00305H13V6.00305C13.0002 6.1344 12.9744 6.26448 12.9241 6.38582C12.8738 6.50715 12.8 6.61735 12.707 6.71005L11.414 8.00305L12.707 9.29605C12.887 9.47605 13 9.72605 13 10.0031V11.0031H12C11.8687 11.0032 11.7386 10.9775 11.6172 10.9272C11.4959 10.8769 11.3857 10.8031 11.293 10.7101L10 9.41705L8.707 10.7101C8.6143 10.8031 8.50409 10.8769 8.38275 10.9272C8.26141 10.9775 8.13134 11.0032 8 11.0031H7V10.0031C7 9.72605 7.112 9.47605 7.293 9.29605L8.586 8.00305L7.293 6.71005C7.19996 6.61735 7.12618 6.50715 7.0759 6.38582C7.02561 6.26448 6.99981 6.1344 7 6.00305V5.00305Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 904 B |
3
res/icons/go-down-symbolic.svg
Normal file
3
res/icons/go-down-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.44039 6.02242L7.50339 12.0844L13.5654 6.02242C15.0034 4.58442 13.5034 3.33442 12.4404 4.39742L7.50339 9.33442L2.56539 4.39742C1.75339 3.58442 0.253389 4.83442 1.44039 6.02242Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 309 B |
3
res/icons/go-up-symbolic.svg
Normal file
3
res/icons/go-up-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.44039 10.063L7.50339 4L13.5654 10.063C15.0034 11.5 13.5034 12.75 12.4404 11.688L7.50339 6.75L2.56539 11.688C1.75339 12.5 0.253389 11.25 1.44039 10.063Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 285 B |
3
res/icons/window-close-symbolic.svg
Normal file
3
res/icons/window-close-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 4.00299C4.73478 4.00299 4.48043 4.10835 4.29289 4.29588C4.10536 4.48342 4 4.73777 4 5.00299C4.00006 5.26819 4.10545 5.5225 4.293 5.70999L6.586 8.00299L4.293 10.296C4.10545 10.4835 4.00006 10.7378 4 11.003C4 11.2682 4.10536 11.5226 4.29289 11.7101C4.48043 11.8976 4.73478 12.003 5 12.003C5.26519 12.0029 5.51951 11.8975 5.707 11.71L8 9.41699L10.283 11.7C10.3762 11.7959 10.4877 11.8721 10.6108 11.9241C10.734 11.9762 10.8663 12.003 11 12.003C11.2652 12.003 11.5196 11.8976 11.7071 11.7101C11.8946 11.5226 12 11.2682 12 11.003C11.9999 10.7378 11.8945 10.4835 11.707 10.296L9.414 8.00299L11.697 5.71999C11.7929 5.6268 11.8691 5.51534 11.9211 5.39218C11.9732 5.26903 12 5.13669 12 5.00299C12 4.73777 11.8946 4.48342 11.7071 4.29588C11.5196 4.10835 11.2652 4.00299 11 4.00299C10.7348 4.00305 10.4805 4.10844 10.293 4.29599L8 6.58899L5.717 4.30599C5.71369 4.30263 5.71036 4.2993 5.707 4.29599C5.51951 4.10844 5.26519 4.00305 5 4.00299Z" fill="#232323"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1 KiB |
49
src/icon_cache.rs
Normal file
49
src/icon_cache.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use cosmic::widget::icon;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct IconCacheKey {
|
||||||
|
name: &'static str,
|
||||||
|
size: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IconCache {
|
||||||
|
cache: HashMap<IconCacheKey, icon::Handle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IconCache {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
|
||||||
|
macro_rules! bundle {
|
||||||
|
($name:expr, $size:expr) => {
|
||||||
|
let data: &'static [u8] = include_bytes!(concat!("../res/icons/", $name, ".svg"));
|
||||||
|
cache.insert(
|
||||||
|
IconCacheKey {
|
||||||
|
name: $name,
|
||||||
|
size: $size,
|
||||||
|
},
|
||||||
|
icon::from_svg_bytes(data).symbolic(true),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle!("edit-clear-symbolic", 16);
|
||||||
|
bundle!("go-down-symbolic", 16);
|
||||||
|
bundle!("go-up-symbolic", 16);
|
||||||
|
bundle!("window-close-symbolic", 16);
|
||||||
|
|
||||||
|
Self { cache }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, name: &'static str, size: u16) -> icon::Icon {
|
||||||
|
let handle = self
|
||||||
|
.cache
|
||||||
|
.entry(IconCacheKey { name, size })
|
||||||
|
.or_insert_with(|| icon::from_name(name).size(size).handle())
|
||||||
|
.clone();
|
||||||
|
icon::icon(handle).size(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
161
src/main.rs
161
src/main.rs
|
|
@ -14,10 +14,10 @@ use cosmic::{
|
||||||
futures::SinkExt,
|
futures::SinkExt,
|
||||||
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
||||||
subscription::{self, Subscription},
|
subscription::{self, Subscription},
|
||||||
window, Event, Length, Padding, Point,
|
window, Alignment, Event, Length, Padding, Point,
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
widget::{self, segmented_button},
|
widget::{self, button, segmented_button},
|
||||||
Application, ApplicationExt, Element,
|
Application, ApplicationExt, Element,
|
||||||
};
|
};
|
||||||
use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight};
|
use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight};
|
||||||
|
|
@ -33,6 +33,9 @@ use tokio::sync::mpsc;
|
||||||
use config::{AppTheme, Config, CONFIG_VERSION};
|
use config::{AppTheme, Config, CONFIG_VERSION};
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
use icon_cache::IconCache;
|
||||||
|
mod icon_cache;
|
||||||
|
|
||||||
mod localize;
|
mod localize;
|
||||||
|
|
||||||
use menu::menu_bar;
|
use menu::menu_bar;
|
||||||
|
|
@ -46,6 +49,15 @@ mod terminal_box;
|
||||||
|
|
||||||
mod terminal_theme;
|
mod terminal_theme;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref ICON_CACHE: Mutex<IconCache> = Mutex::new(IconCache::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icon_cache_get(name: &'static str, size: u16) -> widget::icon::Icon {
|
||||||
|
let mut icon_cache = ICON_CACHE.lock().unwrap();
|
||||||
|
icon_cache.get(name, size)
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
@ -150,6 +162,11 @@ pub enum Message {
|
||||||
DefaultFontWeight(usize),
|
DefaultFontWeight(usize),
|
||||||
DefaultBoldFontWeight(usize),
|
DefaultBoldFontWeight(usize),
|
||||||
DefaultZoomStep(usize),
|
DefaultZoomStep(usize),
|
||||||
|
Find(bool),
|
||||||
|
FindNext,
|
||||||
|
FindPrevious,
|
||||||
|
FindSearchValueChanged(String),
|
||||||
|
Modifiers(Modifiers),
|
||||||
Paste(Option<segmented_button::Entity>),
|
Paste(Option<segmented_button::Entity>),
|
||||||
PasteValue(Option<segmented_button::Entity>, String),
|
PasteValue(Option<segmented_button::Entity>, String),
|
||||||
SelectAll(Option<segmented_button::Entity>),
|
SelectAll(Option<segmented_button::Entity>),
|
||||||
|
|
@ -210,9 +227,14 @@ pub struct App {
|
||||||
theme_names: Vec<String>,
|
theme_names: Vec<String>,
|
||||||
themes: HashMap<String, TermColors>,
|
themes: HashMap<String, TermColors>,
|
||||||
context_page: ContextPage,
|
context_page: ContextPage,
|
||||||
|
terminal_id: widget::Id,
|
||||||
|
find: bool,
|
||||||
|
find_search_id: widget::Id,
|
||||||
|
find_search_value: String,
|
||||||
term_event_tx_opt: Option<mpsc::Sender<(segmented_button::Entity, TermEvent)>>,
|
term_event_tx_opt: Option<mpsc::Sender<(segmented_button::Entity, TermEvent)>>,
|
||||||
term_config: TermConfig,
|
term_config: TermConfig,
|
||||||
show_advanced_font_settings: bool,
|
show_advanced_font_settings: bool,
|
||||||
|
modifiers: Modifiers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -243,6 +265,17 @@ impl App {
|
||||||
self.update_config()
|
self.update_config()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_focus(&self) -> Command<Message> {
|
||||||
|
if self.core.window.show_context {
|
||||||
|
Command::none()
|
||||||
|
} else if self.find {
|
||||||
|
widget::text_input::focus(self.find_search_id.clone())
|
||||||
|
} else {
|
||||||
|
widget::text_input::focus(self.terminal_id.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this any time the tab changes
|
||||||
fn update_title(&mut self) -> Command<Message> {
|
fn update_title(&mut self) -> Command<Message> {
|
||||||
let (header_title, window_title) = match self.tab_model.text(self.tab_model.active()) {
|
let (header_title, window_title) = match self.tab_model.text(self.tab_model.active()) {
|
||||||
Some(tab_title) => (
|
Some(tab_title) => (
|
||||||
|
|
@ -252,7 +285,7 @@ impl App {
|
||||||
None => (String::new(), "COSMIC Terminal".to_string()),
|
None => (String::new(), "COSMIC Terminal".to_string()),
|
||||||
};
|
};
|
||||||
self.set_header_title(header_title);
|
self.set_header_title(header_title);
|
||||||
self.set_window_title(window_title)
|
Command::batch([self.set_window_title(window_title), self.update_focus()])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_curr_font_weights_and_stretches(&mut self) {
|
fn set_curr_font_weights_and_stretches(&mut self) {
|
||||||
|
|
@ -631,9 +664,14 @@ impl Application for App {
|
||||||
theme_names,
|
theme_names,
|
||||||
themes,
|
themes,
|
||||||
context_page: ContextPage::Settings,
|
context_page: ContextPage::Settings,
|
||||||
|
terminal_id: widget::Id::unique(),
|
||||||
|
find: false,
|
||||||
|
find_search_id: widget::Id::unique(),
|
||||||
|
find_search_value: String::new(),
|
||||||
term_config: flags.term_config,
|
term_config: flags.term_config,
|
||||||
term_event_tx_opt: None,
|
term_event_tx_opt: None,
|
||||||
show_advanced_font_settings: false,
|
show_advanced_font_settings: false,
|
||||||
|
modifiers: Modifiers::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
app.set_curr_font_weights_and_stretches();
|
app.set_curr_font_weights_and_stretches();
|
||||||
|
|
@ -642,6 +680,20 @@ impl Application for App {
|
||||||
(app, command)
|
(app, command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: currently the first escape unfocuses, and the second calls this function
|
||||||
|
fn on_escape(&mut self) -> Command<Message> {
|
||||||
|
if self.core.window.show_context {
|
||||||
|
// Close context drawer if open
|
||||||
|
self.core.window.show_context = false;
|
||||||
|
} else if self.find {
|
||||||
|
// Close find if open
|
||||||
|
self.find = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus correct widget
|
||||||
|
self.update_focus()
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle application events here.
|
/// Handle application events here.
|
||||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
|
|
@ -746,6 +798,34 @@ impl Application for App {
|
||||||
log::warn!("failed to find zoom step with index {}", index);
|
log::warn!("failed to find zoom step with index {}", index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Message::Find(find) => {
|
||||||
|
self.find = find;
|
||||||
|
|
||||||
|
// Focus correct input
|
||||||
|
return self.update_focus();
|
||||||
|
}
|
||||||
|
Message::FindNext => {
|
||||||
|
if !self.find_search_value.is_empty() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus correct input
|
||||||
|
return self.update_focus();
|
||||||
|
}
|
||||||
|
Message::FindPrevious => {
|
||||||
|
if !self.find_search_value.is_empty() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus correct input
|
||||||
|
return self.update_focus();
|
||||||
|
}
|
||||||
|
Message::FindSearchValueChanged(value) => {
|
||||||
|
self.find_search_value = value;
|
||||||
|
}
|
||||||
|
Message::Modifiers(modifiers) => {
|
||||||
|
self.modifiers = modifiers;
|
||||||
|
}
|
||||||
Message::Paste(entity_opt) => {
|
Message::Paste(entity_opt) => {
|
||||||
return clipboard::read(move |value_opt| match value_opt {
|
return clipboard::read(move |value_opt| match value_opt {
|
||||||
Some(value) => message::app(Message::PasteValue(entity_opt, value)),
|
Some(value) => message::app(Message::PasteValue(entity_opt, value)),
|
||||||
|
|
@ -1009,9 +1089,11 @@ impl Application for App {
|
||||||
let entity = self.tab_model.active();
|
let entity = self.tab_model.active();
|
||||||
match self.tab_model.data::<Mutex<Terminal>>(entity) {
|
match self.tab_model.data::<Mutex<Terminal>>(entity) {
|
||||||
Some(terminal) => {
|
Some(terminal) => {
|
||||||
let terminal_box = terminal_box(terminal).on_context_menu(move |position_opt| {
|
let terminal_box = terminal_box(terminal)
|
||||||
Message::TabContextMenu(entity, position_opt)
|
.id(self.terminal_id.clone())
|
||||||
});
|
.on_context_menu(move |position_opt| {
|
||||||
|
Message::TabContextMenu(entity, position_opt)
|
||||||
|
});
|
||||||
|
|
||||||
let context_menu = {
|
let context_menu = {
|
||||||
let terminal = terminal.lock().unwrap();
|
let terminal = terminal.lock().unwrap();
|
||||||
|
|
@ -1034,6 +1116,60 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.find {
|
||||||
|
let find_input =
|
||||||
|
widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value)
|
||||||
|
.id(self.find_search_id.clone())
|
||||||
|
.on_input(Message::FindSearchValueChanged)
|
||||||
|
.on_submit(if self.modifiers.contains(Modifiers::SHIFT) {
|
||||||
|
Message::FindPrevious
|
||||||
|
} else {
|
||||||
|
Message::FindNext
|
||||||
|
})
|
||||||
|
.width(Length::Fixed(320.0))
|
||||||
|
.trailing_icon(
|
||||||
|
button(icon_cache_get("edit-clear-symbolic", 16))
|
||||||
|
.on_press(Message::FindSearchValueChanged(String::new()))
|
||||||
|
.style(style::Button::Icon)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
let find_widget = widget::row::with_children(vec![
|
||||||
|
find_input.into(),
|
||||||
|
widget::tooltip(
|
||||||
|
button(icon_cache_get("go-up-symbolic", 16))
|
||||||
|
.on_press(Message::FindPrevious)
|
||||||
|
.padding(space_xxs)
|
||||||
|
.style(style::Button::Icon),
|
||||||
|
fl!("find-previous"),
|
||||||
|
widget::tooltip::Position::Top,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
widget::tooltip(
|
||||||
|
button(icon_cache_get("go-down-symbolic", 16))
|
||||||
|
.on_press(Message::FindNext)
|
||||||
|
.padding(space_xxs)
|
||||||
|
.style(style::Button::Icon),
|
||||||
|
fl!("find-next"),
|
||||||
|
widget::tooltip::Position::Top,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
|
button(icon_cache_get("window-close-symbolic", 16))
|
||||||
|
.on_press(Message::Find(false))
|
||||||
|
.padding(space_xxs)
|
||||||
|
.style(style::Button::Icon)
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.padding(space_xxs)
|
||||||
|
.spacing(space_xxs);
|
||||||
|
|
||||||
|
tab_column = tab_column.push(
|
||||||
|
widget::cosmic_container::container(find_widget)
|
||||||
|
.layer(cosmic_theme::Layer::Primary),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let content: Element<_> = tab_column.into();
|
let content: Element<_> = tab_column.into();
|
||||||
|
|
||||||
// Uncomment to debug layout:
|
// Uncomment to debug layout:
|
||||||
|
|
@ -1068,6 +1204,16 @@ impl Application for App {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
|
key_code: KeyCode::F,
|
||||||
|
modifiers,
|
||||||
|
}) => {
|
||||||
|
if modifiers == Modifiers::CTRL | Modifiers::SHIFT {
|
||||||
|
Some(Message::Find(true))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
Event::Keyboard(KeyEvent::KeyPressed {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
key_code: KeyCode::T,
|
key_code: KeyCode::T,
|
||||||
modifiers,
|
modifiers,
|
||||||
|
|
@ -1118,6 +1264,9 @@ impl Application for App {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||||
|
Some(Message::Modifiers(modifiers))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
subscription::channel(
|
subscription::channel(
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,6 @@ pub fn menu_bar<'a>() -> Element<'a, Message> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let menu_key = |label, key, message| {
|
|
||||||
MenuTree::new(
|
|
||||||
menu_button!(widget::text(label), horizontal_space(Length::Fill), key)
|
|
||||||
.on_press(message),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuBar::new(vec![
|
MenuBar::new(vec![
|
||||||
MenuTree::with_children(
|
MenuTree::with_children(
|
||||||
menu_root(fl!("file")),
|
menu_root(fl!("file")),
|
||||||
|
|
@ -131,7 +124,7 @@ pub fn menu_bar<'a>() -> Element<'a, Message> {
|
||||||
menu_item(fl!("paste"), Message::Paste(None)),
|
menu_item(fl!("paste"), Message::Paste(None)),
|
||||||
menu_item(fl!("select-all"), Message::SelectAll(None)),
|
menu_item(fl!("select-all"), Message::SelectAll(None)),
|
||||||
MenuTree::new(horizontal_rule(1)),
|
MenuTree::new(horizontal_rule(1)),
|
||||||
menu_key(fl!("find"), "Ctrl + F", Message::Todo("find")),
|
menu_item(fl!("find"), Message::Find(true)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
MenuTree::with_children(
|
MenuTree::with_children(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,11 @@ use cosmic::{
|
||||||
layout::{self, Layout},
|
layout::{self, Layout},
|
||||||
renderer::{self, Quad, Renderer as _},
|
renderer::{self, Quad, Renderer as _},
|
||||||
text::Renderer as _,
|
text::Renderer as _,
|
||||||
widget::{self, tree, Widget},
|
widget::{
|
||||||
|
self,
|
||||||
|
operation::{self, Operation, OperationOutputWrapper},
|
||||||
|
tree, Id, Widget,
|
||||||
|
},
|
||||||
Shell,
|
Shell,
|
||||||
},
|
},
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
|
|
@ -37,6 +41,7 @@ use crate::{Terminal, TerminalScroll};
|
||||||
|
|
||||||
pub struct TerminalBox<'a, Message> {
|
pub struct TerminalBox<'a, Message> {
|
||||||
terminal: &'a Mutex<Terminal>,
|
terminal: &'a Mutex<Terminal>,
|
||||||
|
id: Option<Id>,
|
||||||
padding: Padding,
|
padding: Padding,
|
||||||
click_timing: Duration,
|
click_timing: Duration,
|
||||||
context_menu: Option<Point>,
|
context_menu: Option<Point>,
|
||||||
|
|
@ -50,6 +55,7 @@ where
|
||||||
pub fn new(terminal: &'a Mutex<Terminal>) -> Self {
|
pub fn new(terminal: &'a Mutex<Terminal>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
terminal,
|
terminal,
|
||||||
|
id: None,
|
||||||
padding: Padding::new(0.0),
|
padding: Padding::new(0.0),
|
||||||
click_timing: Duration::from_millis(500),
|
click_timing: Duration::from_millis(500),
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
|
|
@ -57,6 +63,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self, id: Id) -> Self {
|
||||||
|
self.id = Some(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||||
self.padding = padding.into();
|
self.padding = padding.into();
|
||||||
self
|
self
|
||||||
|
|
@ -149,6 +160,18 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
tree: &mut widget::Tree,
|
||||||
|
_layout: Layout<'_>,
|
||||||
|
_renderer: &Renderer,
|
||||||
|
operation: &mut dyn Operation<OperationOutputWrapper<Message>>,
|
||||||
|
) {
|
||||||
|
let state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
|
operation.focusable(state, self.id.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
&self,
|
&self,
|
||||||
tree: &widget::Tree,
|
tree: &widget::Tree,
|
||||||
|
|
@ -427,7 +450,7 @@ where
|
||||||
Event::Keyboard(KeyEvent::KeyPressed {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
key_code,
|
key_code,
|
||||||
modifiers,
|
modifiers,
|
||||||
}) => match (
|
}) if state.is_focused => match (
|
||||||
modifiers.logo(),
|
modifiers.logo(),
|
||||||
modifiers.control(),
|
modifiers.control(),
|
||||||
modifiers.alt(),
|
modifiers.alt(),
|
||||||
|
|
@ -670,7 +693,7 @@ where
|
||||||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||||
state.modifiers = modifiers;
|
state.modifiers = modifiers;
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent::CharacterReceived(character)) => {
|
Event::Keyboard(KeyEvent::CharacterReceived(character)) if state.is_focused => {
|
||||||
match (
|
match (
|
||||||
state.modifiers.logo(),
|
state.modifiers.logo(),
|
||||||
state.modifiers.control(),
|
state.modifiers.control(),
|
||||||
|
|
@ -717,6 +740,8 @@ where
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||||
|
state.is_focused = true;
|
||||||
|
|
||||||
// Handle left click drag
|
// Handle left click drag
|
||||||
if let Button::Left = button {
|
if let Button::Left = button {
|
||||||
let x = p.x - self.padding.left;
|
let x = p.x - self.padding.left;
|
||||||
|
|
@ -915,6 +940,7 @@ pub struct State {
|
||||||
modifiers: Modifiers,
|
modifiers: Modifiers,
|
||||||
click: Option<(ClickKind, Instant)>,
|
click: Option<(ClickKind, Instant)>,
|
||||||
dragging: Option<Dragging>,
|
dragging: Option<Dragging>,
|
||||||
|
is_focused: bool,
|
||||||
scroll_pixels: f32,
|
scroll_pixels: f32,
|
||||||
scrollbar_rect: Cell<Rectangle<f32>>,
|
scrollbar_rect: Cell<Rectangle<f32>>,
|
||||||
}
|
}
|
||||||
|
|
@ -926,8 +952,23 @@ impl State {
|
||||||
modifiers: Modifiers::empty(),
|
modifiers: Modifiers::empty(),
|
||||||
click: None,
|
click: None,
|
||||||
dragging: None,
|
dragging: None,
|
||||||
|
is_focused: false,
|
||||||
scroll_pixels: 0.0,
|
scroll_pixels: 0.0,
|
||||||
scrollbar_rect: Cell::new(Rectangle::default()),
|
scrollbar_rect: Cell::new(Rectangle::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl operation::Focusable for State {
|
||||||
|
fn is_focused(&self) -> bool {
|
||||||
|
self.is_focused
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus(&mut self) {
|
||||||
|
self.is_focused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unfocus(&mut self) {
|
||||||
|
self.is_focused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue