mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
parent
ab2db49af5
commit
33abfe34a8
8 changed files with 93 additions and 36 deletions
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Default Command+N keybinding for SpawnNewInstance on macOS
|
||||
- Vi mode for copying text and opening links
|
||||
- `CopySelection` action which copies into selection buffer on Linux/BSD
|
||||
- Option `cursor.thickness` to set terminal cursor thickness
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -318,6 +318,10 @@
|
|||
# window is not focused.
|
||||
#unfocused_hollow: true
|
||||
|
||||
# Thickness of the cursor relative to the cell width as floating point number
|
||||
# from `0.0` to `1.0`.
|
||||
#thickness: 0.15
|
||||
|
||||
# Live config reload (changes require restart)
|
||||
#live_config_reload: true
|
||||
|
||||
|
|
|
@ -20,20 +20,19 @@ use alacritty_terminal::ansi::CursorStyle;
|
|||
|
||||
use font::{BitmapBuffer, Metrics, RasterizedGlyph};
|
||||
|
||||
/// Width/Height of the cursor relative to the font width
|
||||
pub const CURSOR_WIDTH_PERCENTAGE: f64 = 0.15;
|
||||
|
||||
pub fn get_cursor_glyph(
|
||||
cursor: CursorStyle,
|
||||
metrics: Metrics,
|
||||
offset_x: i8,
|
||||
offset_y: i8,
|
||||
is_wide: bool,
|
||||
cursor_thickness: f64,
|
||||
) -> RasterizedGlyph {
|
||||
// Calculate the cell metrics
|
||||
let height = metrics.line_height as i32 + i32::from(offset_y);
|
||||
let mut width = metrics.average_advance as i32 + i32::from(offset_x);
|
||||
let line_width = cmp::max((f64::from(width) * CURSOR_WIDTH_PERCENTAGE).round() as i32, 1);
|
||||
|
||||
let line_width = cmp::max((cursor_thickness * f64::from(width)).round() as i32, 1);
|
||||
|
||||
// Double the cursor width if it's above a double-width glyph
|
||||
if is_wide {
|
||||
|
|
|
@ -286,7 +286,15 @@ impl Display {
|
|||
size_info.cell_height = cell_height;
|
||||
}
|
||||
|
||||
/// Process update events
|
||||
/// Clear glyph cache.
|
||||
fn clear_glyph_cache(&mut self) {
|
||||
let cache = &mut self.glyph_cache;
|
||||
self.renderer.with_loader(|mut api| {
|
||||
cache.clear_glyph_cache(&mut api);
|
||||
});
|
||||
}
|
||||
|
||||
/// Process update events.
|
||||
pub fn handle_update<T>(
|
||||
&mut self,
|
||||
terminal: &mut Term<T>,
|
||||
|
@ -298,6 +306,8 @@ impl Display {
|
|||
// Update font size and cell dimensions
|
||||
if let Some(font) = update_pending.font {
|
||||
self.update_glyph_cache(config, font);
|
||||
} else if update_pending.cursor {
|
||||
self.clear_glyph_cache();
|
||||
}
|
||||
|
||||
let cell_width = self.size_info.cell_width;
|
||||
|
|
|
@ -49,13 +49,14 @@ use crate::window::Window;
|
|||
#[derive(Default, Clone, Debug, PartialEq)]
|
||||
pub struct DisplayUpdate {
|
||||
pub dimensions: Option<PhysicalSize<u32>>,
|
||||
pub message_buffer: Option<()>,
|
||||
pub message_buffer: bool,
|
||||
pub font: Option<Font>,
|
||||
pub cursor: bool,
|
||||
}
|
||||
|
||||
impl DisplayUpdate {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.dimensions.is_none() && self.font.is_none() && self.message_buffer.is_none()
|
||||
self.dimensions.is_none() && self.font.is_none() && !self.message_buffer && !self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,7 +256,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
}
|
||||
|
||||
fn pop_message(&mut self) {
|
||||
self.display_update_pending.message_buffer = Some(());
|
||||
self.display_update_pending.message_buffer = true;
|
||||
self.message_buffer.pop();
|
||||
}
|
||||
|
||||
|
@ -526,7 +527,7 @@ impl<N: Notify + OnResize> Processor<N> {
|
|||
Event::ConfigReload(path) => Self::reload_config(&path, processor),
|
||||
Event::Message(message) => {
|
||||
processor.ctx.message_buffer.push(message);
|
||||
processor.ctx.display_update_pending.message_buffer = Some(());
|
||||
processor.ctx.display_update_pending.message_buffer = true;
|
||||
processor.ctx.terminal.dirty = true;
|
||||
},
|
||||
Event::MouseCursorDirty => processor.reset_mouse_cursor(),
|
||||
|
@ -659,7 +660,7 @@ impl<N: Notify + OnResize> Processor<N> {
|
|||
T: EventListener,
|
||||
{
|
||||
processor.ctx.message_buffer.remove_target(LOG_TARGET_CONFIG);
|
||||
processor.ctx.display_update_pending.message_buffer = Some(());
|
||||
processor.ctx.display_update_pending.message_buffer = true;
|
||||
|
||||
let config = match config::reload_from(&path) {
|
||||
Ok(config) => config,
|
||||
|
@ -671,6 +672,13 @@ impl<N: Notify + OnResize> Processor<N> {
|
|||
|
||||
processor.ctx.terminal.update_config(&config);
|
||||
|
||||
// Reload cursor if we've changed its thickness
|
||||
if (processor.ctx.config.cursor.thickness() - config.cursor.thickness()).abs()
|
||||
> std::f64::EPSILON
|
||||
{
|
||||
processor.ctx.display_update_pending.cursor = true;
|
||||
}
|
||||
|
||||
if processor.ctx.config.font != config.font {
|
||||
// Do not update font size if it has been changed at runtime
|
||||
if *processor.ctx.font_size == processor.ctx.config.font.size {
|
||||
|
|
|
@ -213,10 +213,7 @@ impl GlyphCache {
|
|||
metrics,
|
||||
};
|
||||
|
||||
cache.load_glyphs_for_font(regular, loader);
|
||||
cache.load_glyphs_for_font(bold, loader);
|
||||
cache.load_glyphs_for_font(italic, loader);
|
||||
cache.load_glyphs_for_font(bold_italic, loader);
|
||||
cache.load_common_glyphs(loader);
|
||||
|
||||
Ok(cache)
|
||||
}
|
||||
|
@ -302,17 +299,21 @@ impl GlyphCache {
|
|||
})
|
||||
}
|
||||
|
||||
/// Clear currently cached data in both GL and the registry.
|
||||
pub fn clear_glyph_cache<L: LoadGlyph>(&mut self, loader: &mut L) {
|
||||
loader.clear();
|
||||
self.cache = HashMap::default();
|
||||
self.cursor_cache = HashMap::default();
|
||||
|
||||
self.load_common_glyphs(loader);
|
||||
}
|
||||
|
||||
pub fn update_font_size<L: LoadGlyph>(
|
||||
&mut self,
|
||||
font: config::Font,
|
||||
dpr: f64,
|
||||
loader: &mut L,
|
||||
) -> Result<(), font::Error> {
|
||||
// Clear currently cached data in both GL and the registry
|
||||
loader.clear();
|
||||
self.cache = HashMap::default();
|
||||
self.cursor_cache = HashMap::default();
|
||||
|
||||
// Update dpi scaling
|
||||
self.rasterizer.update_dpr(dpr as f32);
|
||||
|
||||
|
@ -332,10 +333,7 @@ impl GlyphCache {
|
|||
self.bold_italic_key = bold_italic;
|
||||
self.metrics = metrics;
|
||||
|
||||
self.load_glyphs_for_font(regular, loader);
|
||||
self.load_glyphs_for_font(bold, loader);
|
||||
self.load_glyphs_for_font(italic, loader);
|
||||
self.load_glyphs_for_font(bold_italic, loader);
|
||||
self.clear_glyph_cache(loader);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -344,6 +342,14 @@ impl GlyphCache {
|
|||
self.metrics
|
||||
}
|
||||
|
||||
/// Prefetch glyphs that are almost guaranteed to be loaded anyways.
|
||||
fn load_common_glyphs<L: LoadGlyph>(&mut self, loader: &mut L) {
|
||||
self.load_glyphs_for_font(self.font_key, loader);
|
||||
self.load_glyphs_for_font(self.bold_italic_key, loader);
|
||||
self.load_glyphs_for_font(self.italic_key, loader);
|
||||
self.load_glyphs_for_font(self.bold_italic_key, loader);
|
||||
}
|
||||
|
||||
// Calculate font metrics without access to a glyph cache
|
||||
pub fn static_metrics(font: Font, dpr: f64) -> Result<font::Metrics, font::Error> {
|
||||
let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
|
||||
|
@ -1019,6 +1025,7 @@ impl<'a, C> RenderApi<'a, C> {
|
|||
self.config.font.offset.x,
|
||||
self.config.font.offset.y,
|
||||
cursor_key.is_wide,
|
||||
self.config.cursor.thickness(),
|
||||
))
|
||||
});
|
||||
self.add_render_item(cell, glyph);
|
||||
|
|
|
@ -40,6 +40,7 @@ use crate::term::color::Rgb;
|
|||
|
||||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config";
|
||||
const MAX_SCROLLBACK_LINES: u32 = 100_000;
|
||||
const DEFAULT_CURSOR_THICKNESS: f32 = 0.15;
|
||||
|
||||
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
|
||||
|
||||
|
@ -67,7 +68,7 @@ pub struct Config<T> {
|
|||
|
||||
/// Background opacity from 0.0 to 1.0
|
||||
#[serde(default, deserialize_with = "failure_default")]
|
||||
background_opacity: Alpha,
|
||||
background_opacity: Percentage,
|
||||
|
||||
/// Window configuration
|
||||
#[serde(default, deserialize_with = "failure_default")]
|
||||
|
@ -213,7 +214,7 @@ impl<T> Config<T> {
|
|||
|
||||
#[inline]
|
||||
pub fn background_opacity(&self) -> f32 {
|
||||
self.background_opacity.0
|
||||
self.background_opacity.0 as f32
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,20 +243,47 @@ impl Default for EscapeChars {
|
|||
}
|
||||
|
||||
#[serde(default)]
|
||||
#[derive(Deserialize, Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Cursor {
|
||||
#[serde(deserialize_with = "failure_default")]
|
||||
pub style: CursorStyle,
|
||||
#[serde(deserialize_with = "option_explicit_none")]
|
||||
pub vi_mode_style: Option<CursorStyle>,
|
||||
#[serde(deserialize_with = "deserialize_cursor_thickness")]
|
||||
thickness: Percentage,
|
||||
#[serde(deserialize_with = "failure_default")]
|
||||
unfocused_hollow: DefaultTrueBool,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
#[inline]
|
||||
pub fn unfocused_hollow(self) -> bool {
|
||||
self.unfocused_hollow.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn thickness(self) -> f64 {
|
||||
self.thickness.0 as f64
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Cursor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
style: Default::default(),
|
||||
vi_mode_style: Default::default(),
|
||||
thickness: Percentage::new(DEFAULT_CURSOR_THICKNESS),
|
||||
unfocused_hollow: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_cursor_thickness<'a, D>(deserializer: D) -> Result<Percentage, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
Ok(Percentage::deserialize(Value::deserialize(deserializer)?)
|
||||
.unwrap_or_else(|_| Percentage::new(DEFAULT_CURSOR_THICKNESS)))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
|
@ -300,13 +328,13 @@ pub struct Delta<T: Default + PartialEq + Eq> {
|
|||
pub y: T,
|
||||
}
|
||||
|
||||
/// Wrapper around f32 that represents an alpha value between 0.0 and 1.0
|
||||
/// Wrapper around f32 that represents a percentage value between 0.0 and 1.0.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Alpha(f32);
|
||||
pub struct Percentage(f32);
|
||||
|
||||
impl Alpha {
|
||||
impl Percentage {
|
||||
pub fn new(value: f32) -> Self {
|
||||
Alpha(if value < 0.0 {
|
||||
Percentage(if value < 0.0 {
|
||||
0.0
|
||||
} else if value > 1.0 {
|
||||
1.0
|
||||
|
@ -316,18 +344,18 @@ impl Alpha {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Alpha {
|
||||
impl Default for Percentage {
|
||||
fn default() -> Self {
|
||||
Alpha(1.0)
|
||||
Percentage(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Alpha {
|
||||
impl<'a> Deserialize<'a> for Percentage {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
Ok(Alpha::new(f32::deserialize(deserializer)?))
|
||||
Ok(Percentage::new(f32::deserialize(deserializer)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ struct ScrollingMultiplier(u8);
|
|||
|
||||
impl Default for ScrollingMultiplier {
|
||||
fn default() -> Self {
|
||||
ScrollingMultiplier(3)
|
||||
Self(3)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ struct ScrollingHistory(u32);
|
|||
|
||||
impl Default for ScrollingHistory {
|
||||
fn default() -> Self {
|
||||
ScrollingHistory(10_000)
|
||||
Self(10_000)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue