Skip to content

Commit

Permalink
support builtin_box_drawing config
Browse files Browse the repository at this point in the history
  • Loading branch information
raphamorim committed Feb 22, 2025
1 parent 0207638 commit c141024
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 79 deletions.
4 changes: 4 additions & 0 deletions docs/docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ program = "vi"
args = []
```

## builtin-box-drawing

When true, Rio will use a custom built-in font for box drawing characters `(Unicode points U+2500 - U+259F)`, legacy computing symbols `(U+1FB00 - U+1FB3B)`, and powerline symbols `(U+E0B0 - U+E0B3)`.

## colors

Defining colors in the configuration file will not have any effect if you're using a theme.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ language: 'en'
- OSC 1337 Escape sequences to set user vars for tracking additional shell state.
- Use [GoReleaser](https://goreleaser.com) to build & release Rio ([#921](https://github.com/raphamorim/rio/pull/921))
(thanks [@caarlos0](https://github.com/caarlos0) and [@vedantmgoyal9](https://github.com/vedantmgoyal9))
- Fix issue whenever the first main font cannot be found.
- Updated `windows-sys` to `v0.59`.
- To match the corresponding changes in `windows-sys`, the `HWND`, `HMONITOR`, and `HMENU` types now alias to `*mut c_void` instead of `isize`.

Expand Down
52 changes: 37 additions & 15 deletions frontends/rioterm/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use unicode_width::UnicodeWidthChar;
pub struct Renderer {
is_vi_mode_enabled: bool,
draw_bold_text_with_light_colors: bool,
builtin_box_drawing: bool,
pub named_colors: Colors,
pub colors: List,
pub navigation: ScreenNavigation,
Expand All @@ -50,6 +51,11 @@ pub struct Renderer {
active_search: Option<String>,
}

const POWERLINE_TRIANGLE_LTR: char = '\u{e0b0}';
// const POWERLINE_ARROW_LTR: char = '\u{e0b1}';
// const POWERLINE_TRIANGLE_RTL: char = '\u{e0b2}';
const POWERLINE_ARROW_RTL: char = '\u{e0b3}';

impl Renderer {
pub fn new(
config: &Config,
Expand Down Expand Up @@ -79,6 +85,7 @@ impl Renderer {
}

Renderer {
builtin_box_drawing: config.builtin_box_drawing,
draw_bold_text_with_light_colors: config.draw_bold_text_with_light_colors,
macos_use_unified_titlebar: config.window.macos_use_unified_titlebar,
config_blinking_interval: config.cursor.blinking_interval.clamp(350, 1200),
Expand Down Expand Up @@ -332,23 +339,38 @@ impl Renderer {
let mut width = square.c.width().unwrap_or(1) as f32;
let mut font_ctx = self.font_context.inner.lock();

// There is no simple way to define what's emoji
// could have to refer to the Unicode tables. However it could
// be leading to misleading results. For example if we used
// unicode and internationalization functionalities like
// https://github.com/open-i18n/rust-unic/, then characters
// like "◼" would be valid emojis. For a terminal context,
// the character "◼" is not an emoji and should be treated as
// single width. So, we completely rely on what font is
// being used and then set width 2 for it.
if let Some((font_id, is_emoji)) =
font_ctx.find_best_font_match(square_content, &style)
{
style.font_id = font_id;
if is_emoji {
width = 2.0;
match (self.builtin_box_drawing, square_content) {
// Box drawing characters and block elements.
(true, '\u{2500}'..='\u{259f}' | '\u{1fb00}'..='\u{1fb3b}') => {
style.font_id = 0;
}

// Powerline symbols: '','','',''
(true, POWERLINE_TRIANGLE_LTR..=POWERLINE_ARROW_RTL) => {
style.font_id = 0;
}

_ => {
// There is no simple way to define what's emoji
// could have to refer to the Unicode tables. However it could
// be leading to misleading results. For example if we used
// unicode and internationalization functionalities like
// https://github.com/open-i18n/rust-unic/, then characters
// like "◼" would be valid emojis. For a terminal context,
// the character "◼" is not an emoji and should be treated as
// single width. So, we completely rely on what font is
// being used and then set width 2 for it.
if let Some((font_id, is_emoji)) =
font_ctx.find_best_font_match(square_content, &style)
{
style.font_id = font_id;
if is_emoji {
width = 2.0;
}
}
}
}

style.width = width;

self.font_cache.insert(
Expand Down
3 changes: 3 additions & 0 deletions rio-backend/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ pub struct Config {
pub renderer: Renderer,
#[serde(default = "bool::default", rename = "draw-bold-text-with-light-colors")]
pub draw_bold_text_with_light_colors: bool,
#[serde(default = "default_bool_true", rename = "builtin-box-drawing")]
pub builtin_box_drawing: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
Expand Down Expand Up @@ -507,6 +509,7 @@ impl Default for Config {
confirm_before_quit: true,
hide_cursor_when_typing: false,
draw_bold_text_with_light_colors: false,
builtin_box_drawing: true,
}
}
}
Expand Down
36 changes: 34 additions & 2 deletions sugarloaf/examples/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use sugarloaf::{
};

fn main() {
let width = 600.0;
let height = 400.0;
let width = 1200.0;
let height = 500.0;
let window_event_loop = rio_window::event_loop::EventLoop::new().unwrap();
let mut application = Application::new(&window_event_loop, width, height);
let _ = application.run(window_event_loop);
Expand Down Expand Up @@ -265,6 +265,38 @@ impl ApplicationHandler for Application {
..FragmentStyle::default()
},
)
.new_line()

/*
Box drawing alignment tests: █
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
▝▀▘▙▄▟
*/

.add_text("Box drawing alignment tests: █", FragmentStyle::default())
.new_line()
.add_text("╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳", FragmentStyle::default())
.new_line()
.add_text("║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳", FragmentStyle::default())
.new_line()
.add_text("║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳", FragmentStyle::default())
.new_line()
.add_text("╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳", FragmentStyle::default())
.new_line()
.add_text("║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎", FragmentStyle::default())
.new_line()
.add_text("║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏", FragmentStyle::default())
.new_line()
.add_text("╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█", FragmentStyle::default())
.new_line()
.add_text(" ▝▀▘▙▄▟", FragmentStyle::default())
.build();

sugarloaf.set_objects(vec![Object::RichText(RichText {
Expand Down
131 changes: 69 additions & 62 deletions sugarloaf/src/font/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub fn lookup_for_font_match(
let mut font_synth = Synthesis::default();
let fonts_len: usize = library.inner.len();

for font_id in 0..fonts_len {
// Note: zero it's a built-in fallback font
for font_id in 1..fonts_len {
let mut is_emoji = false;

if let Some(font) = library.inner.get(&font_id) {
Expand Down Expand Up @@ -261,16 +262,22 @@ impl FontLibraryData {
let mut db = loader::Database::new();
db.load_system_fonts();

// Index zero is a fallback font, it will not be looked up
// unless it's flagged to allow builtin fallback
// this is used mostly by box drawing characters
self.insert(load_fallback_from_memory(&spec.regular));

match find_font(&db, spec.regular, false, false) {
FindResult::Found(data) => {
self.insert(data);
}
FindResult::NotFound(spec) => {
if !spec.is_default_family() {
fonts_not_fount.push(spec);
} else {
self.insert(load_fallback_from_memory(&spec));
fonts_not_fount.push(spec.to_owned());
}

// The first font should always have a fallback
self.insert(load_fallback_from_memory(&spec));
}
}

Expand Down Expand Up @@ -313,64 +320,64 @@ impl FontLibraryData {
}
}

for fallback in fallbacks::external_fallbacks() {
match find_font(
&db,
SugarloafFont {
family: fallback,
..SugarloafFont::default()
},
true,
false,
) {
FindResult::Found(data) => {
self.insert(data);
}
FindResult::NotFound(spec) => {
// Fallback should not add errors
tracing::info!("{:?}", spec);
}
}
}

if let Some(emoji_font) = spec.emoji {
match find_font(&db, emoji_font, true, true) {
FindResult::Found(data) => {
self.insert(data);
}
FindResult::NotFound(spec) => {
self.insert(FontData::from_slice(FONT_TWEMOJI_EMOJI, true).unwrap());
if !spec.is_default_family() {
fonts_not_fount.push(spec);
}
}
}
} else {
self.insert(FontData::from_slice(FONT_TWEMOJI_EMOJI, true).unwrap());
}

for extra_font in spec.extras {
match find_font(
&db,
SugarloafFont {
family: extra_font.family,
style: extra_font.style,
weight: extra_font.weight,
width: extra_font.width,
},
true,
true,
) {
FindResult::Found(data) => {
self.insert(data);
}
FindResult::NotFound(spec) => {
fonts_not_fount.push(spec);
}
}
}

self.insert(FontData::from_slice(FONT_SYMBOLS_NERD_FONT_MONO, false).unwrap());
// for fallback in fallbacks::external_fallbacks() {
// match find_font(
// &db,
// SugarloafFont {
// family: fallback,
// ..SugarloafFont::default()
// },
// true,
// false,
// ) {
// FindResult::Found(data) => {
// self.insert(data);
// }
// FindResult::NotFound(spec) => {
// // Fallback should not add errors
// tracing::info!("{:?}", spec);
// }
// }
// }

// if let Some(emoji_font) = spec.emoji {
// match find_font(&db, emoji_font, true, true) {
// FindResult::Found(data) => {
// self.insert(data);
// }
// FindResult::NotFound(spec) => {
// self.insert(FontData::from_slice(FONT_TWEMOJI_EMOJI, true).unwrap());
// if !spec.is_default_family() {
// fonts_not_fount.push(spec);
// }
// }
// }
// } else {
// self.insert(FontData::from_slice(FONT_TWEMOJI_EMOJI, true).unwrap());
// }

// for extra_font in spec.extras {
// match find_font(
// &db,
// SugarloafFont {
// family: extra_font.family,
// style: extra_font.style,
// weight: extra_font.weight,
// width: extra_font.width,
// },
// true,
// true,
// ) {
// FindResult::Found(data) => {
// self.insert(data);
// }
// FindResult::NotFound(spec) => {
// fonts_not_fount.push(spec);
// }
// }
// }

// self.insert(FontData::from_slice(FONT_SYMBOLS_NERD_FONT_MONO, false).unwrap());

if let Some(ui_spec) = spec.ui {
match find_font(&db, ui_spec, false, false) {
Expand Down

0 comments on commit c141024

Please sign in to comment.