mirror of
https://github.com/wez/wezterm.git
synced 2024-12-24 13:52:55 +03:00
add PaneSelect key assignment
This is still a bit of a WIP, but this commit: * Introduces a new "Modal" concept to the GUI layer. The intent is that modal intercepts key and mouse events while active, and renders over the top of the rest of the normal display. I think there might be a couple of cases where key events skirt through this, but this is good enough as a first step. Also, the render is forced into layer 1 which has some funny side effects: if the modal choses to render transparent, it will poke a hole in the window because all the rendering happens together: there aren't distinct layer compositing passes. * Add a new PaneSelect action that is implemented as a modal. It uses quickselect style alphabet -> pane label generation and renders the labels over ~the middle of each pane using an enlarged version of the window frame font. Typing the label will activate that pane. Escape will cancel the modal. More styling and docs will follow in a later commit. refs: #1975
This commit is contained in:
parent
efb8d14064
commit
5bf736bd21
@ -90,6 +90,9 @@ pub struct Config {
|
|||||||
#[dynamic(default)]
|
#[dynamic(default)]
|
||||||
pub window_frame: WindowFrameConfig,
|
pub window_frame: WindowFrameConfig,
|
||||||
|
|
||||||
|
#[dynamic(default = "default_pane_select_font_size")]
|
||||||
|
pub pane_select_font_size: f64,
|
||||||
|
|
||||||
#[dynamic(default)]
|
#[dynamic(default)]
|
||||||
pub tab_bar_style: TabBarStyle,
|
pub tab_bar_style: TabBarStyle,
|
||||||
|
|
||||||
@ -1131,6 +1134,10 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_pane_select_font_size() -> f64 {
|
||||||
|
36.0
|
||||||
|
}
|
||||||
|
|
||||||
fn default_swallow_mouse_click_on_window_focus() -> bool {
|
fn default_swallow_mouse_click_on_window_focus() -> bool {
|
||||||
cfg!(target_os = "macos")
|
cfg!(target_os = "macos")
|
||||||
}
|
}
|
||||||
|
@ -264,6 +264,13 @@ impl Default for ClipboardPasteSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
|
||||||
|
pub struct PaneSelectArguments {
|
||||||
|
/// Overrides the main quick_select_alphabet config
|
||||||
|
#[dynamic(default)]
|
||||||
|
pub alphabet: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, FromDynamic, ToDynamic)]
|
||||||
pub struct QuickSelectArguments {
|
pub struct QuickSelectArguments {
|
||||||
/// Overrides the main quick_select_alphabet config
|
/// Overrides the main quick_select_alphabet config
|
||||||
@ -372,6 +379,7 @@ pub enum KeyAssignment {
|
|||||||
CopyMode(CopyModeAssignment),
|
CopyMode(CopyModeAssignment),
|
||||||
RotatePanes(RotationDirection),
|
RotatePanes(RotationDirection),
|
||||||
SplitPane(SplitPane),
|
SplitPane(SplitPane),
|
||||||
|
PaneSelect(PaneSelectArguments),
|
||||||
}
|
}
|
||||||
impl_lua_conversion_dynamic!(KeyAssignment);
|
impl_lua_conversion_dynamic!(KeyAssignment);
|
||||||
|
|
||||||
|
@ -429,6 +429,7 @@ struct FontConfigInner {
|
|||||||
built_in: RefCell<Arc<FontDatabase>>,
|
built_in: RefCell<Arc<FontDatabase>>,
|
||||||
no_glyphs: RefCell<HashSet<char>>,
|
no_glyphs: RefCell<HashSet<char>>,
|
||||||
title_font: RefCell<Option<Rc<LoadedFont>>>,
|
title_font: RefCell<Option<Rc<LoadedFont>>>,
|
||||||
|
pane_select_font: RefCell<Option<Rc<LoadedFont>>>,
|
||||||
fallback_channel: RefCell<Option<Sender<FallbackResolveInfo>>>,
|
fallback_channel: RefCell<Option<Sender<FallbackResolveInfo>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,6 +448,7 @@ impl FontConfigInner {
|
|||||||
locator,
|
locator,
|
||||||
metrics: RefCell::new(None),
|
metrics: RefCell::new(None),
|
||||||
title_font: RefCell::new(None),
|
title_font: RefCell::new(None),
|
||||||
|
pane_select_font: RefCell::new(None),
|
||||||
font_scale: RefCell::new(1.0),
|
font_scale: RefCell::new(1.0),
|
||||||
dpi: RefCell::new(dpi),
|
dpi: RefCell::new(dpi),
|
||||||
config: RefCell::new(config.clone()),
|
config: RefCell::new(config.clone()),
|
||||||
@ -463,6 +465,7 @@ impl FontConfigInner {
|
|||||||
// Config was reloaded, invalidate our caches
|
// Config was reloaded, invalidate our caches
|
||||||
fonts.clear();
|
fonts.clear();
|
||||||
self.title_font.borrow_mut().take();
|
self.title_font.borrow_mut().take();
|
||||||
|
self.pane_select_font.borrow_mut().take();
|
||||||
self.metrics.borrow_mut().take();
|
self.metrics.borrow_mut().take();
|
||||||
self.no_glyphs.borrow_mut().clear();
|
self.no_glyphs.borrow_mut().clear();
|
||||||
*self.font_dirs.borrow_mut() = Arc::new(FontDatabase::with_font_dirs(config)?);
|
*self.font_dirs.borrow_mut() = Arc::new(FontDatabase::with_font_dirs(config)?);
|
||||||
@ -544,18 +547,15 @@ impl FontConfigInner {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title_font(&self, myself: &Rc<Self>) -> anyhow::Result<Rc<LoadedFont>> {
|
fn make_title_font_impl(
|
||||||
|
&self,
|
||||||
|
myself: &Rc<Self>,
|
||||||
|
pref_size: Option<f64>,
|
||||||
|
) -> anyhow::Result<Rc<LoadedFont>> {
|
||||||
let config = self.config.borrow();
|
let config = self.config.borrow();
|
||||||
|
|
||||||
let mut title_font = self.title_font.borrow_mut();
|
|
||||||
|
|
||||||
if let Some(entry) = title_font.as_ref() {
|
|
||||||
return Ok(Rc::clone(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (sys_font, sys_size) = self.compute_title_font(&config);
|
let (sys_font, sys_size) = self.compute_title_font(&config);
|
||||||
|
|
||||||
let font_size = config.window_frame.font_size.unwrap_or(sys_size);
|
let font_size = pref_size.unwrap_or(sys_size);
|
||||||
|
|
||||||
let text_style = config
|
let text_style = config
|
||||||
.window_frame
|
.window_frame
|
||||||
@ -591,11 +591,41 @@ impl FontConfigInner {
|
|||||||
id: alloc_font_id(),
|
id: alloc_font_id(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title_font(&self, myself: &Rc<Self>) -> anyhow::Result<Rc<LoadedFont>> {
|
||||||
|
let config = self.config.borrow();
|
||||||
|
|
||||||
|
let mut title_font = self.title_font.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(entry) = title_font.as_ref() {
|
||||||
|
return Ok(Rc::clone(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
let loaded = self.make_title_font_impl(myself, config.window_frame.font_size)?;
|
||||||
|
|
||||||
title_font.replace(Rc::clone(&loaded));
|
title_font.replace(Rc::clone(&loaded));
|
||||||
|
|
||||||
Ok(loaded)
|
Ok(loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pane_select_font(&self, myself: &Rc<Self>) -> anyhow::Result<Rc<LoadedFont>> {
|
||||||
|
let config = self.config.borrow();
|
||||||
|
|
||||||
|
let mut pane_select_font = self.pane_select_font.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(entry) = pane_select_font.as_ref() {
|
||||||
|
return Ok(Rc::clone(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
let loaded = self.make_title_font_impl(myself, Some(config.pane_select_font_size))?;
|
||||||
|
|
||||||
|
pane_select_font.replace(Rc::clone(&loaded));
|
||||||
|
|
||||||
|
Ok(loaded)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_font_helper_impl(
|
fn resolve_font_helper_impl(
|
||||||
&self,
|
&self,
|
||||||
attributes: &[FontAttributes],
|
attributes: &[FontAttributes],
|
||||||
@ -934,6 +964,10 @@ impl FontConfiguration {
|
|||||||
self.inner.title_font(&self.inner)
|
self.inner.title_font(&self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pane_select_font(&self) -> anyhow::Result<Rc<LoadedFont>> {
|
||||||
|
self.inner.pane_select_font(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a text style, load (with caching) the font that best
|
/// Given a text style, load (with caching) the font that best
|
||||||
/// matches according to the fontconfig pattern.
|
/// matches according to the fontconfig pattern.
|
||||||
pub fn resolve_font(&self, style: &TextStyle) -> anyhow::Result<Rc<LoadedFont>> {
|
pub fn resolve_font(&self, style: &TextStyle) -> anyhow::Result<Rc<LoadedFont>> {
|
||||||
|
@ -54,7 +54,7 @@ const PATTERNS: [&str; 14] = [
|
|||||||
/// This function computes a set of labels for a given alphabet.
|
/// This function computes a set of labels for a given alphabet.
|
||||||
/// It is derived from https://github.com/fcsonline/tmux-thumbs/blob/master/src/alphabets.rs
|
/// It is derived from https://github.com/fcsonline/tmux-thumbs/blob/master/src/alphabets.rs
|
||||||
/// which is Copyright (c) 2019 Ferran Basora and provided under the MIT license
|
/// which is Copyright (c) 2019 Ferran Basora and provided under the MIT license
|
||||||
fn compute_labels_for_alphabet(alphabet: &str, num_matches: usize) -> Vec<String> {
|
pub fn compute_labels_for_alphabet(alphabet: &str, num_matches: usize) -> Vec<String> {
|
||||||
let alphabet = alphabet
|
let alphabet = alphabet
|
||||||
.chars()
|
.chars()
|
||||||
.map(|c| c.to_lowercase().to_string())
|
.map(|c| c.to_lowercase().to_string())
|
||||||
|
@ -278,6 +278,13 @@ impl super::TermWindow {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
if is_down {
|
||||||
|
return modal.key_down(term_key, tw_raw_modifiers, self).is_ok();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let res = if is_down {
|
let res = if is_down {
|
||||||
pane.key_down(term_key, tw_raw_modifiers)
|
pane.key_down(term_key, tw_raw_modifiers)
|
||||||
} else {
|
} else {
|
||||||
@ -512,6 +519,13 @@ impl super::TermWindow {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
if window_key.key_is_down {
|
||||||
|
modal.key_down(key, modifiers, self).ok();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let res = if let Some(encoded) = self.encode_win32_input(&pane, &window_key) {
|
let res = if let Some(encoded) = self.encode_win32_input(&pane, &window_key) {
|
||||||
if self.config.debug_key_events {
|
if self.config.debug_key_events {
|
||||||
log::info!("Encoded input as {:?}", encoded);
|
log::info!("Encoded input as {:?}", encoded);
|
||||||
|
@ -18,6 +18,7 @@ use crate::selection::Selection;
|
|||||||
use crate::shapecache::*;
|
use crate::shapecache::*;
|
||||||
use crate::tabbar::{TabBarItem, TabBarState};
|
use crate::tabbar::{TabBarItem, TabBarState};
|
||||||
use crate::termwindow::keyevent::KeyTableState;
|
use crate::termwindow::keyevent::KeyTableState;
|
||||||
|
use crate::termwindow::modal::Modal;
|
||||||
use ::wezterm_term::input::{ClickPosition, MouseButton as TMB};
|
use ::wezterm_term::input::{ClickPosition, MouseButton as TMB};
|
||||||
use ::window::*;
|
use ::window::*;
|
||||||
use anyhow::{anyhow, ensure, Context};
|
use anyhow::{anyhow, ensure, Context};
|
||||||
@ -60,7 +61,9 @@ use wezterm_term::{Alert, StableRowIndex, TerminalConfiguration};
|
|||||||
pub mod box_model;
|
pub mod box_model;
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
mod keyevent;
|
mod keyevent;
|
||||||
|
pub mod modal;
|
||||||
mod mouseevent;
|
mod mouseevent;
|
||||||
|
pub mod paneselect;
|
||||||
mod prevcursor;
|
mod prevcursor;
|
||||||
mod render;
|
mod render;
|
||||||
pub mod resize;
|
pub mod resize;
|
||||||
@ -382,6 +385,8 @@ pub struct TermWindow {
|
|||||||
ui_items: Vec<UIItem>,
|
ui_items: Vec<UIItem>,
|
||||||
dragging: Option<(UIItem, MouseEvent)>,
|
dragging: Option<(UIItem, MouseEvent)>,
|
||||||
|
|
||||||
|
modal: RefCell<Option<Rc<dyn Modal>>>,
|
||||||
|
|
||||||
event_states: HashMap<String, EventState>,
|
event_states: HashMap<String, EventState>,
|
||||||
has_animation: RefCell<Option<Instant>>,
|
has_animation: RefCell<Option<Instant>>,
|
||||||
/// We use this to attempt to do something reasonable
|
/// We use this to attempt to do something reasonable
|
||||||
@ -818,6 +823,7 @@ impl TermWindow {
|
|||||||
last_ui_item: None,
|
last_ui_item: None,
|
||||||
is_click_to_focus_window: false,
|
is_click_to_focus_window: false,
|
||||||
key_table_state: KeyTableState::default(),
|
key_table_state: KeyTableState::default(),
|
||||||
|
modal: RefCell::new(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let tw = Rc::new(RefCell::new(myself));
|
let tw = Rc::new(RefCell::new(myself));
|
||||||
@ -887,6 +893,7 @@ impl TermWindow {
|
|||||||
|
|
||||||
crate::update::start_update_checker();
|
crate::update::start_update_checker();
|
||||||
front_end().record_known_window(window, mux_window_id);
|
front_end().record_known_window(window, mux_window_id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,6 +1012,7 @@ impl TermWindow {
|
|||||||
match notif {
|
match notif {
|
||||||
TermWindowNotif::InvalidateShapeCache => {
|
TermWindowNotif::InvalidateShapeCache => {
|
||||||
self.shape_cache.borrow_mut().clear();
|
self.shape_cache.borrow_mut().clear();
|
||||||
|
self.invalidate_modal();
|
||||||
window.invalidate();
|
window.invalidate();
|
||||||
}
|
}
|
||||||
TermWindowNotif::PerformAssignment {
|
TermWindowNotif::PerformAssignment {
|
||||||
@ -1140,6 +1148,7 @@ impl TermWindow {
|
|||||||
self.tab_state.borrow_mut().clear();
|
self.tab_state.borrow_mut().clear();
|
||||||
self.current_highlight.take();
|
self.current_highlight.take();
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
|
|
||||||
let mux = Mux::get().expect("to be main thread with mux running");
|
let mux = Mux::get().expect("to be main thread with mux running");
|
||||||
if let Some(window) = mux.get_window(self.mux_window_id) {
|
if let Some(window) = mux.get_window(self.mux_window_id) {
|
||||||
@ -1503,6 +1512,7 @@ impl TermWindow {
|
|||||||
self.shape_cache.borrow_mut().clear();
|
self.shape_cache.borrow_mut().clear();
|
||||||
self.fancy_tab_bar.take();
|
self.fancy_tab_bar.take();
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
self.input_map = InputMap::new(&config);
|
self.input_map = InputMap::new(&config);
|
||||||
self.leader_is_down = None;
|
self.leader_is_down = None;
|
||||||
let dimensions = self.dimensions;
|
let dimensions = self.dimensions;
|
||||||
@ -1529,9 +1539,27 @@ impl TermWindow {
|
|||||||
window.invalidate();
|
window.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.invalidate_modal();
|
||||||
self.emit_window_event("window-config-reloaded", None);
|
self.emit_window_event("window-config-reloaded", None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidate_modal(&mut self) {
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
modal.reconfigure(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel_modal(&self) {
|
||||||
|
self.modal.borrow_mut().take();
|
||||||
|
if let Some(window) = self.window.as_ref() {
|
||||||
|
window.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modal(&self) -> Option<Rc<dyn Modal>> {
|
||||||
|
self.modal.borrow().as_ref().map(|m| Rc::clone(&m))
|
||||||
|
}
|
||||||
|
|
||||||
fn update_scrollbar(&mut self) {
|
fn update_scrollbar(&mut self) {
|
||||||
if !self.show_scroll_bar {
|
if !self.show_scroll_bar {
|
||||||
return;
|
return;
|
||||||
@ -1630,6 +1658,7 @@ impl TermWindow {
|
|||||||
if new_tab_bar != self.tab_bar {
|
if new_tab_bar != self.tab_bar {
|
||||||
self.tab_bar = new_tab_bar;
|
self.tab_bar = new_tab_bar;
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
if let Some(window) = self.window.as_ref() {
|
if let Some(window) = self.window.as_ref() {
|
||||||
window.invalidate();
|
window.invalidate();
|
||||||
}
|
}
|
||||||
@ -2043,6 +2072,12 @@ impl TermWindow {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
use KeyAssignment::*;
|
use KeyAssignment::*;
|
||||||
|
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
if modal.perform_assignment(assignment, self) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if pane.perform_assignment(assignment) {
|
if pane.perform_assignment(assignment) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -2538,6 +2573,10 @@ impl TermWindow {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
PaneSelect(args) => {
|
||||||
|
let modal = crate::termwindow::paneselect::PaneSelector::new(self, args);
|
||||||
|
self.modal.borrow_mut().replace(Rc::new(modal));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
29
wezterm-gui/src/termwindow/modal.rs
Normal file
29
wezterm-gui/src/termwindow/modal.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use crate::termwindow::box_model::ComputedElement;
|
||||||
|
use crate::TermWindow;
|
||||||
|
use config::keyassignment::KeyAssignment;
|
||||||
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
|
use std::cell::Ref;
|
||||||
|
use wezterm_term::{KeyCode, KeyModifiers, MouseEvent};
|
||||||
|
|
||||||
|
pub trait Modal: Downcast {
|
||||||
|
fn perform_assignment(
|
||||||
|
&self,
|
||||||
|
_assignment: &KeyAssignment,
|
||||||
|
_term_window: &mut TermWindow,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn mouse_event(&self, event: MouseEvent, term_window: &mut TermWindow) -> anyhow::Result<()>;
|
||||||
|
fn key_down(
|
||||||
|
&self,
|
||||||
|
key: KeyCode,
|
||||||
|
mods: KeyModifiers,
|
||||||
|
term_window: &mut TermWindow,
|
||||||
|
) -> anyhow::Result<()>;
|
||||||
|
fn computed_element(
|
||||||
|
&self,
|
||||||
|
term_window: &mut TermWindow,
|
||||||
|
) -> anyhow::Result<Ref<[ComputedElement]>>;
|
||||||
|
fn reconfigure(&self, term_window: &mut TermWindow);
|
||||||
|
}
|
||||||
|
impl_downcast!(Modal);
|
186
wezterm-gui/src/termwindow/paneselect.rs
Normal file
186
wezterm-gui/src/termwindow/paneselect.rs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
use crate::color::LinearRgba;
|
||||||
|
use crate::termwindow::box_model::*;
|
||||||
|
use crate::termwindow::modal::Modal;
|
||||||
|
use crate::termwindow::DimensionContext;
|
||||||
|
use crate::utilsprites::RenderMetrics;
|
||||||
|
use crate::TermWindow;
|
||||||
|
use config::keyassignment::{KeyAssignment, PaneSelectArguments};
|
||||||
|
use mux::Mux;
|
||||||
|
use std::cell::{Ref, RefCell};
|
||||||
|
use wezterm_term::{KeyCode, KeyModifiers, MouseEvent};
|
||||||
|
|
||||||
|
pub struct PaneSelector {
|
||||||
|
element: RefCell<Option<Vec<ComputedElement>>>,
|
||||||
|
labels: RefCell<Vec<String>>,
|
||||||
|
selection: RefCell<String>,
|
||||||
|
alphabet: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneSelector {
|
||||||
|
pub fn new(term_window: &mut TermWindow, args: &PaneSelectArguments) -> Self {
|
||||||
|
let alphabet = if args.alphabet.is_empty() {
|
||||||
|
term_window.config.quick_select_alphabet.clone()
|
||||||
|
} else {
|
||||||
|
args.alphabet.clone()
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
element: RefCell::new(None),
|
||||||
|
labels: RefCell::new(vec![]),
|
||||||
|
selection: RefCell::new(String::new()),
|
||||||
|
alphabet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute(
|
||||||
|
term_window: &mut TermWindow,
|
||||||
|
alphabet: &str,
|
||||||
|
) -> anyhow::Result<(Vec<ComputedElement>, Vec<String>)> {
|
||||||
|
let font = term_window
|
||||||
|
.fonts
|
||||||
|
.pane_select_font()
|
||||||
|
.expect("to resolve pane selection font");
|
||||||
|
let metrics = RenderMetrics::with_font_metrics(&font.metrics());
|
||||||
|
|
||||||
|
let top_bar_height = if term_window.show_tab_bar && !term_window.config.tab_bar_at_bottom {
|
||||||
|
term_window.tab_bar_pixel_height().unwrap()
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
let (padding_left, padding_top) = term_window.padding_left_top();
|
||||||
|
let border = term_window.get_os_border();
|
||||||
|
let top_pixel_y = top_bar_height + padding_top + border.top.get() as f32;
|
||||||
|
|
||||||
|
let panes = term_window.get_panes_to_render();
|
||||||
|
let labels =
|
||||||
|
crate::overlay::quickselect::compute_labels_for_alphabet(alphabet, panes.len());
|
||||||
|
|
||||||
|
let mut elements = vec![];
|
||||||
|
for pos in panes {
|
||||||
|
let caption = labels[pos.index].clone();
|
||||||
|
let element =
|
||||||
|
Element::new(&font, ElementContent::Text(caption)).colors(ElementColors {
|
||||||
|
border: BorderColor::default(),
|
||||||
|
bg: LinearRgba::with_srgba(0x00, 0x00, 0x00, 0xff).into(),
|
||||||
|
text: LinearRgba::with_srgba(0xff, 0xff, 0xff, 0xff).into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let dimensions = term_window.dimensions;
|
||||||
|
let pane_dims = pos.pane.get_dimensions();
|
||||||
|
|
||||||
|
let computed = term_window.compute_element(
|
||||||
|
&LayoutContext {
|
||||||
|
height: DimensionContext {
|
||||||
|
dpi: dimensions.dpi as f32,
|
||||||
|
pixel_max: dimensions.pixel_height as f32,
|
||||||
|
pixel_cell: metrics.cell_size.height as f32,
|
||||||
|
},
|
||||||
|
width: DimensionContext {
|
||||||
|
dpi: dimensions.dpi as f32,
|
||||||
|
pixel_max: dimensions.pixel_width as f32,
|
||||||
|
pixel_cell: metrics.cell_size.width as f32,
|
||||||
|
},
|
||||||
|
bounds: euclid::rect(
|
||||||
|
padding_left
|
||||||
|
+ ((pos.left as f32 + pane_dims.cols as f32 / 2.)
|
||||||
|
* term_window.render_metrics.cell_size.width as f32),
|
||||||
|
top_pixel_y
|
||||||
|
+ ((pos.top as f32 + pane_dims.viewport_rows as f32 / 2.)
|
||||||
|
* term_window.render_metrics.cell_size.height as f32),
|
||||||
|
pane_dims.cols as f32 * term_window.render_metrics.cell_size.width as f32,
|
||||||
|
pane_dims.viewport_rows as f32
|
||||||
|
* term_window.render_metrics.cell_size.height as f32,
|
||||||
|
),
|
||||||
|
metrics: &metrics,
|
||||||
|
gl_state: term_window.render_state.as_ref().unwrap(),
|
||||||
|
},
|
||||||
|
&element,
|
||||||
|
)?;
|
||||||
|
elements.push(computed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((elements, labels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Modal for PaneSelector {
|
||||||
|
fn perform_assignment(
|
||||||
|
&self,
|
||||||
|
_assignment: &KeyAssignment,
|
||||||
|
_term_window: &mut TermWindow,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_event(&self, _event: MouseEvent, _term_window: &mut TermWindow) -> anyhow::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_down(
|
||||||
|
&self,
|
||||||
|
key: KeyCode,
|
||||||
|
mods: KeyModifiers,
|
||||||
|
term_window: &mut TermWindow,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match (key, mods) {
|
||||||
|
(KeyCode::Escape, KeyModifiers::NONE) => {
|
||||||
|
term_window.cancel_modal();
|
||||||
|
}
|
||||||
|
(KeyCode::Char(c), KeyModifiers::NONE) | (KeyCode::Char(c), KeyModifiers::SHIFT) => {
|
||||||
|
// Type to add to the selection
|
||||||
|
let mut selection = self.selection.borrow_mut();
|
||||||
|
selection.push(c);
|
||||||
|
|
||||||
|
// and if we have a complete match, activate that pane
|
||||||
|
if let Some(pane_index) = self.labels.borrow().iter().position(|s| s == &*selection)
|
||||||
|
{
|
||||||
|
let mux = Mux::get().unwrap();
|
||||||
|
let tab = match mux.get_active_tab_for_window(term_window.mux_window_id) {
|
||||||
|
Some(tab) => tab,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let tab_id = tab.tab_id();
|
||||||
|
|
||||||
|
if term_window.tab_state(tab_id).overlay.is_none() {
|
||||||
|
let panes = tab.iter_panes();
|
||||||
|
if panes.iter().position(|p| p.index == pane_index).is_some() {
|
||||||
|
tab.set_active_idx(pane_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
term_window.cancel_modal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(KeyCode::Backspace, KeyModifiers::NONE) => {
|
||||||
|
// Backspace to edit the selection
|
||||||
|
let mut selection = self.selection.borrow_mut();
|
||||||
|
selection.pop();
|
||||||
|
}
|
||||||
|
(KeyCode::Char('u'), KeyModifiers::CTRL) => {
|
||||||
|
// CTRL-u to clear the selection
|
||||||
|
let mut selection = self.selection.borrow_mut();
|
||||||
|
selection.clear();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn computed_element(
|
||||||
|
&self,
|
||||||
|
term_window: &mut TermWindow,
|
||||||
|
) -> anyhow::Result<Ref<[ComputedElement]>> {
|
||||||
|
if self.element.borrow().is_none() {
|
||||||
|
let (element, labels) = Self::compute(term_window, &self.alphabet)?;
|
||||||
|
self.element.borrow_mut().replace(element);
|
||||||
|
*self.labels.borrow_mut() = labels;
|
||||||
|
}
|
||||||
|
Ok(Ref::map(self.element.borrow(), |v| {
|
||||||
|
v.as_ref().unwrap().as_slice()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconfigure(&self, _term_window: &mut TermWindow) {
|
||||||
|
self.element.borrow_mut().take();
|
||||||
|
}
|
||||||
|
}
|
@ -221,6 +221,7 @@ impl super::TermWindow {
|
|||||||
self.render_state.as_mut().unwrap().vb[vb_idx].need_more_quads()
|
self.render_state.as_mut().unwrap().vb[vb_idx].need_more_quads()
|
||||||
{
|
{
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
|
|
||||||
// Round up to next multiple of 1024 that is >=
|
// Round up to next multiple of 1024 that is >=
|
||||||
// the number of needed quads for this frame
|
// the number of needed quads for this frame
|
||||||
@ -263,6 +264,7 @@ impl super::TermWindow {
|
|||||||
self.recreate_texture_atlas(Some(size))
|
self.recreate_texture_atlas(Some(size))
|
||||||
};
|
};
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
if self.allow_images {
|
if self.allow_images {
|
||||||
@ -283,6 +285,7 @@ impl super::TermWindow {
|
|||||||
}
|
}
|
||||||
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
|
self.invalidate_modal();
|
||||||
self.shape_cache.borrow_mut().clear();
|
self.shape_cache.borrow_mut().clear();
|
||||||
} else {
|
} else {
|
||||||
log::error!("paint_opengl_pass failed: {:#}", err);
|
log::error!("paint_opengl_pass failed: {:#}", err);
|
||||||
@ -821,6 +824,24 @@ impl super::TermWindow {
|
|||||||
Ok(computed)
|
Ok(computed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paint_modal(&mut self) -> anyhow::Result<()> {
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
for computed in modal.computed_element(self)?.iter() {
|
||||||
|
let mut ui_items = computed.ui_items();
|
||||||
|
|
||||||
|
let gl_state = self.render_state.as_ref().unwrap();
|
||||||
|
let vb = &gl_state.vb[1];
|
||||||
|
let mut vb_mut = vb.current_vb_mut();
|
||||||
|
let mut layer1 = vb.map(&mut vb_mut);
|
||||||
|
self.render_element(&computed, &mut layer1, None)?;
|
||||||
|
|
||||||
|
self.ui_items.append(&mut ui_items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn paint_fancy_tab_bar(&self) -> anyhow::Result<Vec<UIItem>> {
|
fn paint_fancy_tab_bar(&self) -> anyhow::Result<Vec<UIItem>> {
|
||||||
let computed = self
|
let computed = self
|
||||||
.fancy_tab_bar
|
.fancy_tab_bar
|
||||||
@ -1656,6 +1677,7 @@ impl super::TermWindow {
|
|||||||
self.paint_tab_bar()?;
|
self.paint_tab_bar()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.paint_modal()?;
|
||||||
self.paint_window_borders()?;
|
self.paint_window_borders()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -51,6 +51,9 @@ impl super::TermWindow {
|
|||||||
} else {
|
} else {
|
||||||
self.scaling_changed(dimensions, self.fonts.get_font_scale(), window);
|
self.scaling_changed(dimensions, self.fonts.get_font_scale(), window);
|
||||||
}
|
}
|
||||||
|
if let Some(modal) = self.get_modal() {
|
||||||
|
modal.reconfigure(self);
|
||||||
|
}
|
||||||
self.emit_window_event("window-resized", None);
|
self.emit_window_event("window-resized", None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user