Add find stub
This commit is contained in:
parent
8766819ea8
commit
928269a239
9 changed files with 266 additions and 17 deletions
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,
|
||||
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
||||
subscription::{self, Subscription},
|
||||
window, Event, Length, Padding, Point,
|
||||
window, Alignment, Event, Length, Padding, Point,
|
||||
},
|
||||
style,
|
||||
widget::{self, segmented_button},
|
||||
widget::{self, button, segmented_button},
|
||||
Application, ApplicationExt, Element,
|
||||
};
|
||||
use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight};
|
||||
|
|
@ -33,6 +33,9 @@ use tokio::sync::mpsc;
|
|||
use config::{AppTheme, Config, CONFIG_VERSION};
|
||||
mod config;
|
||||
|
||||
use icon_cache::IconCache;
|
||||
mod icon_cache;
|
||||
|
||||
mod localize;
|
||||
|
||||
use menu::menu_bar;
|
||||
|
|
@ -46,6 +49,15 @@ mod terminal_box;
|
|||
|
||||
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
|
||||
#[rustfmt::skip]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
@ -150,6 +162,11 @@ pub enum Message {
|
|||
DefaultFontWeight(usize),
|
||||
DefaultBoldFontWeight(usize),
|
||||
DefaultZoomStep(usize),
|
||||
Find(bool),
|
||||
FindNext,
|
||||
FindPrevious,
|
||||
FindSearchValueChanged(String),
|
||||
Modifiers(Modifiers),
|
||||
Paste(Option<segmented_button::Entity>),
|
||||
PasteValue(Option<segmented_button::Entity>, String),
|
||||
SelectAll(Option<segmented_button::Entity>),
|
||||
|
|
@ -210,9 +227,14 @@ pub struct App {
|
|||
theme_names: Vec<String>,
|
||||
themes: HashMap<String, TermColors>,
|
||||
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_config: TermConfig,
|
||||
show_advanced_font_settings: bool,
|
||||
modifiers: Modifiers,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -243,6 +265,17 @@ impl App {
|
|||
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> {
|
||||
let (header_title, window_title) = match self.tab_model.text(self.tab_model.active()) {
|
||||
Some(tab_title) => (
|
||||
|
|
@ -252,7 +285,7 @@ impl App {
|
|||
None => (String::new(), "COSMIC Terminal".to_string()),
|
||||
};
|
||||
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) {
|
||||
|
|
@ -631,9 +664,14 @@ impl Application for App {
|
|||
theme_names,
|
||||
themes,
|
||||
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_event_tx_opt: None,
|
||||
show_advanced_font_settings: false,
|
||||
modifiers: Modifiers::empty(),
|
||||
};
|
||||
|
||||
app.set_curr_font_weights_and_stretches();
|
||||
|
|
@ -642,6 +680,20 @@ impl Application for App {
|
|||
(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.
|
||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||
match message {
|
||||
|
|
@ -746,6 +798,34 @@ impl Application for App {
|
|||
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) => {
|
||||
return clipboard::read(move |value_opt| match value_opt {
|
||||
Some(value) => message::app(Message::PasteValue(entity_opt, value)),
|
||||
|
|
@ -1009,9 +1089,11 @@ impl Application for App {
|
|||
let entity = self.tab_model.active();
|
||||
match self.tab_model.data::<Mutex<Terminal>>(entity) {
|
||||
Some(terminal) => {
|
||||
let terminal_box = terminal_box(terminal).on_context_menu(move |position_opt| {
|
||||
Message::TabContextMenu(entity, position_opt)
|
||||
});
|
||||
let terminal_box = terminal_box(terminal)
|
||||
.id(self.terminal_id.clone())
|
||||
.on_context_menu(move |position_opt| {
|
||||
Message::TabContextMenu(entity, position_opt)
|
||||
});
|
||||
|
||||
let context_menu = {
|
||||
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();
|
||||
|
||||
// Uncomment to debug layout:
|
||||
|
|
@ -1068,6 +1204,16 @@ impl Application for App {
|
|||
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 {
|
||||
key_code: KeyCode::T,
|
||||
modifiers,
|
||||
|
|
@ -1118,6 +1264,9 @@ impl Application for App {
|
|||
None
|
||||
}
|
||||
}
|
||||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||
Some(Message::Modifiers(modifiers))
|
||||
}
|
||||
_ => None,
|
||||
}),
|
||||
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![
|
||||
MenuTree::with_children(
|
||||
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!("select-all"), Message::SelectAll(None)),
|
||||
MenuTree::new(horizontal_rule(1)),
|
||||
menu_key(fl!("find"), "Ctrl + F", Message::Todo("find")),
|
||||
menu_item(fl!("find"), Message::Find(true)),
|
||||
],
|
||||
),
|
||||
MenuTree::with_children(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,11 @@ use cosmic::{
|
|||
layout::{self, Layout},
|
||||
renderer::{self, Quad, Renderer as _},
|
||||
text::Renderer as _,
|
||||
widget::{self, tree, Widget},
|
||||
widget::{
|
||||
self,
|
||||
operation::{self, Operation, OperationOutputWrapper},
|
||||
tree, Id, Widget,
|
||||
},
|
||||
Shell,
|
||||
},
|
||||
theme::Theme,
|
||||
|
|
@ -37,6 +41,7 @@ use crate::{Terminal, TerminalScroll};
|
|||
|
||||
pub struct TerminalBox<'a, Message> {
|
||||
terminal: &'a Mutex<Terminal>,
|
||||
id: Option<Id>,
|
||||
padding: Padding,
|
||||
click_timing: Duration,
|
||||
context_menu: Option<Point>,
|
||||
|
|
@ -50,6 +55,7 @@ where
|
|||
pub fn new(terminal: &'a Mutex<Terminal>) -> Self {
|
||||
Self {
|
||||
terminal,
|
||||
id: None,
|
||||
padding: Padding::new(0.0),
|
||||
click_timing: Duration::from_millis(500),
|
||||
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 {
|
||||
self.padding = padding.into();
|
||||
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(
|
||||
&self,
|
||||
tree: &widget::Tree,
|
||||
|
|
@ -427,7 +450,7 @@ where
|
|||
Event::Keyboard(KeyEvent::KeyPressed {
|
||||
key_code,
|
||||
modifiers,
|
||||
}) => match (
|
||||
}) if state.is_focused => match (
|
||||
modifiers.logo(),
|
||||
modifiers.control(),
|
||||
modifiers.alt(),
|
||||
|
|
@ -670,7 +693,7 @@ where
|
|||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||
state.modifiers = modifiers;
|
||||
}
|
||||
Event::Keyboard(KeyEvent::CharacterReceived(character)) => {
|
||||
Event::Keyboard(KeyEvent::CharacterReceived(character)) if state.is_focused => {
|
||||
match (
|
||||
state.modifiers.logo(),
|
||||
state.modifiers.control(),
|
||||
|
|
@ -717,6 +740,8 @@ where
|
|||
}
|
||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
state.is_focused = true;
|
||||
|
||||
// Handle left click drag
|
||||
if let Button::Left = button {
|
||||
let x = p.x - self.padding.left;
|
||||
|
|
@ -915,6 +940,7 @@ pub struct State {
|
|||
modifiers: Modifiers,
|
||||
click: Option<(ClickKind, Instant)>,
|
||||
dragging: Option<Dragging>,
|
||||
is_focused: bool,
|
||||
scroll_pixels: f32,
|
||||
scrollbar_rect: Cell<Rectangle<f32>>,
|
||||
}
|
||||
|
|
@ -926,8 +952,23 @@ impl State {
|
|||
modifiers: Modifiers::empty(),
|
||||
click: None,
|
||||
dragging: None,
|
||||
is_focused: false,
|
||||
scroll_pixels: 0.0,
|
||||
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