feat: refine sidebar type and SSH editor
This commit is contained in:
1 parent
67de759252
commit
992cc4f375
3 files changed
+216
-93
No files matched your search
@@ -1236,13 +1236,15 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
// inside the header band to keep clearance from the join.
|
||||
let (pnl_x, pnl_y, _, _) = tab_layout.panel;
|
||||
let tabs_chevron = if d.nebula_tabs_section_open { "\u{eab4}" } else { "\u{eab6}" };
|
||||
const SECTION_TITLE_SCALE: f32 = design_tokens::type_scale::SECTION_CAPTION;
|
||||
const SECTION_TITLE_SCALE: f32 = 0.82;
|
||||
let section_title_tracking = s(0.65);
|
||||
let section_title_flags = nebula_terminal::term::cell::Flags::BOLD;
|
||||
d.renderer.draw_doc_text(
|
||||
d.renderer.draw_doc_text_tracked(
|
||||
&size,
|
||||
pnl_x + s(16.0),
|
||||
pnl_y + s(22.0),
|
||||
SECTION_TITLE_SCALE,
|
||||
section_title_tracking,
|
||||
TXT_DIM,
|
||||
section_title_flags,
|
||||
&format!("TABS {tabs_chevron}"),
|
||||
@@ -1293,7 +1295,9 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
} else {
|
||||
d.nebula_tab_labels.get(index).map(String::as_str).unwrap_or(".")
|
||||
};
|
||||
let max_chars = ((tab_w - reserved).max(cell_w) / cell_w).floor() as usize;
|
||||
let row_tracking = s(0.35);
|
||||
let max_chars =
|
||||
((tab_w - reserved).max(cell_w) / (cell_w + row_tracking)).floor() as usize;
|
||||
let label = truncate_tab_label(label, max_chars.max(1));
|
||||
|
||||
// Input box + selection/caret when renaming this tab. These
|
||||
@@ -1304,6 +1308,7 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
// why the rename box was invisible. Draw them now, before the
|
||||
// label glyphs, so box/selection sit under the text.
|
||||
let renaming_this = d.nebula_tab_rename.as_ref().is_some_and(|(i, _)| *i == index);
|
||||
let label_tracking = if renaming_this { 0.0 } else { row_tracking };
|
||||
let select_all = renaming_this && d.nebula_tab_rename_select_all;
|
||||
if renaming_this {
|
||||
let input_pad = s(4.0);
|
||||
@@ -1400,10 +1405,12 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
d.window.set_ime_cursor_area_px(caret_px, row_y, cell_w, tab_h);
|
||||
}
|
||||
|
||||
d.renderer.draw_chrome_text(
|
||||
d.renderer.draw_doc_text_tracked(
|
||||
&size,
|
||||
text_x,
|
||||
cy,
|
||||
1.0,
|
||||
label_tracking,
|
||||
if renaming_this {
|
||||
if select_all {
|
||||
Rgb::new(255, 255, 255) // White on the blue selection
|
||||
@@ -1413,6 +1420,7 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
} else {
|
||||
color
|
||||
},
|
||||
nebula_terminal::term::cell::Flags::empty(),
|
||||
&label,
|
||||
&mut d.glyph_cache,
|
||||
);
|
||||
@@ -1447,11 +1455,12 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
if tab_layout.hosts_header.2 > 0.0 {
|
||||
let (hh_x, hh_y, _, hh_h) = tab_layout.hosts_header;
|
||||
let hosts_chevron = if d.nebula_hosts_section_open { "\u{eab4}" } else { "\u{eab6}" };
|
||||
d.renderer.draw_doc_text(
|
||||
d.renderer.draw_doc_text_tracked(
|
||||
&size,
|
||||
hh_x + s(16.0),
|
||||
hh_y + (hh_h - cell_h * SECTION_TITLE_SCALE) / 2.0,
|
||||
SECTION_TITLE_SCALE,
|
||||
section_title_tracking,
|
||||
TXT_DIM,
|
||||
section_title_flags,
|
||||
&format!("SSH HOSTS {hosts_chevron}"),
|
||||
@@ -1531,14 +1540,27 @@ pub(super) fn draw_chrome(d: &mut Display) {
|
||||
// aliases run past the hover pill.
|
||||
let text_x = hx + s(14.0);
|
||||
let right = hx + hw - s(42.0);
|
||||
let max_cols = (((right - text_x) / cell_w).floor() as usize).saturating_sub(2);
|
||||
let row_tracking = s(0.35);
|
||||
let max_cols = (((right - text_x) / (cell_w + row_tracking)).floor() as usize)
|
||||
.saturating_sub(2);
|
||||
let label = truncate_tab_label(name, max_cols.max(1));
|
||||
d.renderer.draw_chrome_text(
|
||||
&size,
|
||||
text_x,
|
||||
cy,
|
||||
color,
|
||||
&format!("\u{f489} {label}"),
|
||||
"\u{f489}",
|
||||
&mut d.glyph_cache,
|
||||
);
|
||||
d.renderer.draw_doc_text_tracked(
|
||||
&size,
|
||||
text_x + cell_w * 2.0,
|
||||
cy,
|
||||
1.0,
|
||||
row_tracking,
|
||||
color,
|
||||
nebula_terminal::term::cell::Flags::empty(),
|
||||
&label,
|
||||
&mut d.glyph_cache,
|
||||
);
|
||||
if pinned {
|
||||
|
||||
@@ -35,27 +35,28 @@ impl Display {
|
||||
};
|
||||
let (show_password, show_keys) = auth_sections(editor.auth);
|
||||
|
||||
let box_w = s(700.0).min(size.width() - s(32.0));
|
||||
let box_w = s(520.0).min(size.width() - s(32.0));
|
||||
let desired_h = if show_password && show_keys {
|
||||
610.0
|
||||
540.0
|
||||
} else if show_keys {
|
||||
520.0
|
||||
460.0
|
||||
} else if show_password {
|
||||
440.0
|
||||
400.0
|
||||
} else {
|
||||
370.0
|
||||
330.0
|
||||
};
|
||||
let box_h = s(desired_h).min(size.height() - s(32.0));
|
||||
let bx = (size.width() - box_w) * 0.5;
|
||||
let resting_y = (size.height() - box_h) * 0.5;
|
||||
let by = resting_y - (1.0 - progress) * s(14.0);
|
||||
let pad = s(28.0);
|
||||
let field_h = s(42.0);
|
||||
let pad = s(24.0);
|
||||
let field_h = s(40.0);
|
||||
let field_w = box_w - pad * 2.0;
|
||||
let destination = (bx + pad, by + s(76.0), field_w, field_h);
|
||||
let auth_y = destination.1 + destination.3 + s(42.0);
|
||||
let auth_gap = s(6.0);
|
||||
let auth_w = (field_w - auth_gap * 4.0) / 5.0;
|
||||
let auth_y = destination.1 + destination.3 + s(40.0);
|
||||
let auth_track = (destination.0, auth_y, field_w, s(40.0));
|
||||
let auth_pad = s(3.0);
|
||||
let auth_w = (field_w - auth_pad * 2.0) / 5.0;
|
||||
let auth_modes = [
|
||||
SshAuthMode::Auto,
|
||||
SshAuthMode::Password,
|
||||
@@ -66,10 +67,15 @@ impl Display {
|
||||
let auth = std::array::from_fn(|index| {
|
||||
(
|
||||
auth_modes[index],
|
||||
(destination.0 + index as f32 * (auth_w + auth_gap), auth_y, auth_w, s(34.0)),
|
||||
(
|
||||
auth_track.0 + auth_pad + index as f32 * auth_w,
|
||||
auth_track.1 + auth_pad,
|
||||
auth_w,
|
||||
auth_track.3 - auth_pad * 2.0,
|
||||
),
|
||||
)
|
||||
});
|
||||
let content_y = auth_y + s(76.0);
|
||||
let content_y = auth_y + s(72.0);
|
||||
let zero = (0.0, 0.0, 0.0, 0.0);
|
||||
let password =
|
||||
if show_password { (destination.0, content_y, field_w, field_h) } else { zero };
|
||||
@@ -95,16 +101,17 @@ impl Display {
|
||||
zero
|
||||
};
|
||||
|
||||
let key_header_y = if show_password { save_toggle.1 + s(56.0) } else { content_y };
|
||||
let key_header_y = if show_password { save_toggle.1 + s(54.0) } else { content_y };
|
||||
let add_private_key = if show_keys {
|
||||
(destination.0 + field_w - s(150.0), key_header_y - s(8.0), s(150.0), s(32.0))
|
||||
(destination.0 + field_w - s(126.0), key_header_y - s(8.0), s(126.0), s(32.0))
|
||||
} else {
|
||||
zero
|
||||
};
|
||||
let key_rows_y = key_header_y + s(30.0);
|
||||
let footer_y = by + box_h - s(58.0);
|
||||
let footer_top = footer_y - s(16.0);
|
||||
let available_rows =
|
||||
(((footer_y - s(22.0) - key_rows_y) / s(36.0)).floor() as isize).max(1) as usize;
|
||||
(((footer_top - s(16.0) - key_rows_y) / s(36.0)).floor() as isize).max(1) as usize;
|
||||
let visible_start = editor.private_keys.len().saturating_sub(available_rows);
|
||||
let visible_keys = if show_keys {
|
||||
editor
|
||||
@@ -128,13 +135,37 @@ impl Display {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let primary_label = "保存 Enter";
|
||||
let cancel_label = "取消 Esc";
|
||||
let button_pad = s(16.0);
|
||||
let primary_w = s(112.0).max(text_width(primary_label) + button_pad * 2.0);
|
||||
let cancel_w = s(112.0).max(text_width(cancel_label) + button_pad * 2.0);
|
||||
let primary_action = "保存";
|
||||
let primary_key = "Enter";
|
||||
let cancel_action = "取消";
|
||||
let cancel_key = "Esc";
|
||||
let key_pad = s(6.0);
|
||||
let label_gap = s(8.0);
|
||||
let button_pad = s(14.0);
|
||||
let primary_key_w = text_width(primary_key) + key_pad * 2.0;
|
||||
let cancel_key_w = text_width(cancel_key) + key_pad * 2.0;
|
||||
let primary_w =
|
||||
s(108.0).max(text_width(primary_action) + label_gap + primary_key_w + button_pad * 2.0);
|
||||
let cancel_w =
|
||||
s(100.0).max(text_width(cancel_action) + label_gap + cancel_key_w + button_pad * 2.0);
|
||||
let primary = (bx + box_w - pad - primary_w, footer_y, primary_w, s(36.0));
|
||||
let cancel = (primary.0 - s(12.0) - cancel_w, primary.1, cancel_w, s(36.0));
|
||||
let primary_group_w = text_width(primary_action) + label_gap + primary_key_w;
|
||||
let primary_group_x = primary.0 + (primary.2 - primary_group_w) * 0.5;
|
||||
let primary_key_rect = (
|
||||
primary_group_x + text_width(primary_action) + label_gap,
|
||||
primary.1 + s(7.0),
|
||||
primary_key_w,
|
||||
primary.3 - s(14.0),
|
||||
);
|
||||
let cancel_group_w = text_width(cancel_action) + label_gap + cancel_key_w;
|
||||
let cancel_group_x = cancel.0 + (cancel.2 - cancel_group_w) * 0.5;
|
||||
let cancel_key_rect = (
|
||||
cancel_group_x + text_width(cancel_action) + label_gap,
|
||||
cancel.1 + s(7.0),
|
||||
cancel_key_w,
|
||||
cancel.3 - s(14.0),
|
||||
);
|
||||
|
||||
self.nebula_ssh_editor_rects = Some(SshEditorRects {
|
||||
destination,
|
||||
@@ -170,6 +201,8 @@ impl Display {
|
||||
skin.hairline,
|
||||
),
|
||||
UiQuad::solid(bx, by, box_w, box_h, s(12.0), skin.panel),
|
||||
UiQuad::solid(bx, footer_top, box_w, box_h - (footer_top - by), 0.0, skin.surface),
|
||||
UiQuad::solid(bx, footer_top, box_w, s(1.0), 0.0, skin.hairline),
|
||||
];
|
||||
input_quads(
|
||||
&mut quads,
|
||||
@@ -191,31 +224,33 @@ impl Display {
|
||||
scale,
|
||||
);
|
||||
}
|
||||
quads.push(UiQuad::solid(
|
||||
auth_track.0 - s(1.0),
|
||||
auth_track.1 - s(1.0),
|
||||
auth_track.2 + s(2.0),
|
||||
auth_track.3 + s(2.0),
|
||||
s(9.0),
|
||||
skin.hairline,
|
||||
));
|
||||
quads.push(UiQuad::solid(
|
||||
auth_track.0,
|
||||
auth_track.1,
|
||||
auth_track.2,
|
||||
auth_track.3,
|
||||
s(8.0),
|
||||
skin.surface,
|
||||
));
|
||||
for (mode, rect) in auth {
|
||||
let active = editor.auth == mode;
|
||||
let hovered = self.nebula_ssh_editor_hover == SshEditorHit::Auth(mode);
|
||||
quads.push(UiQuad::solid(
|
||||
rect.0,
|
||||
rect.1,
|
||||
rect.2,
|
||||
rect.3,
|
||||
s(7.0),
|
||||
if active {
|
||||
skin.accent_soft
|
||||
} else if hovered {
|
||||
skin.hover
|
||||
} else {
|
||||
skin.input
|
||||
},
|
||||
));
|
||||
if active {
|
||||
if active || hovered {
|
||||
quads.push(UiQuad::solid(
|
||||
rect.0,
|
||||
rect.1 + rect.3 - s(2.0),
|
||||
rect.1,
|
||||
rect.2,
|
||||
s(2.0),
|
||||
0.0,
|
||||
accent,
|
||||
rect.3,
|
||||
s(6.0),
|
||||
if active { skin.hover_strong } else { skin.hover },
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -248,6 +283,14 @@ impl Display {
|
||||
));
|
||||
}
|
||||
if show_keys {
|
||||
quads.push(UiQuad::solid(
|
||||
add_private_key.0 - s(1.0),
|
||||
add_private_key.1 - s(1.0),
|
||||
add_private_key.2 + s(2.0),
|
||||
add_private_key.3 + s(2.0),
|
||||
s(7.0),
|
||||
skin.hairline,
|
||||
));
|
||||
quads.push(UiQuad::solid(
|
||||
add_private_key.0,
|
||||
add_private_key.1,
|
||||
@@ -257,7 +300,7 @@ impl Display {
|
||||
if self.nebula_ssh_editor_hover == SshEditorHit::AddPrivateKey {
|
||||
skin.hover
|
||||
} else {
|
||||
skin.input
|
||||
skin.surface
|
||||
},
|
||||
));
|
||||
for (index, row, remove) in &visible_keys {
|
||||
@@ -283,6 +326,17 @@ impl Display {
|
||||
&skin,
|
||||
scale,
|
||||
);
|
||||
for rect in [cancel_key_rect, primary_key_rect] {
|
||||
quads.push(UiQuad::solid(
|
||||
rect.0 - s(1.0),
|
||||
rect.1 - s(1.0),
|
||||
rect.2 + s(2.0),
|
||||
rect.3 + s(2.0),
|
||||
s(5.0),
|
||||
skin.hairline,
|
||||
));
|
||||
quads.push(UiQuad::solid(rect.0, rect.1, rect.2, rect.3, s(4.0), skin.input));
|
||||
}
|
||||
|
||||
let caret_field = if editor.field == SshEditorField::Password && show_password {
|
||||
SshEditorField::Password
|
||||
@@ -316,7 +370,7 @@ impl Display {
|
||||
&size,
|
||||
bx + pad,
|
||||
by + s(20.0),
|
||||
1.35,
|
||||
1.15,
|
||||
skin.ink_strong,
|
||||
Flags::empty(),
|
||||
if editor.original_destination.is_some() {
|
||||
@@ -330,12 +384,22 @@ impl Display {
|
||||
&size,
|
||||
destination.0,
|
||||
destination.1 - cell_h - s(5.0),
|
||||
skin.ink,
|
||||
"连接地址",
|
||||
glyph_cache,
|
||||
);
|
||||
self.renderer.draw_doc_text(
|
||||
&size,
|
||||
destination.0 + s(82.0),
|
||||
destination.1 - cell_h - s(3.0),
|
||||
0.72,
|
||||
if editor.error.is_some() {
|
||||
if skin.is_light { Rgb::new(207, 34, 46) } else { Rgb::new(248, 81, 73) }
|
||||
} else {
|
||||
skin.ink_dim
|
||||
},
|
||||
editor.error.as_deref().unwrap_or("用户名@地址(非 22 端口可用 ssh://user@host:port)"),
|
||||
Flags::empty(),
|
||||
editor.error.as_deref().unwrap_or("user@host · 非 22 端口用 ssh://"),
|
||||
glyph_cache,
|
||||
);
|
||||
self.renderer.draw_chrome_text(
|
||||
@@ -360,7 +424,11 @@ impl Display {
|
||||
&size,
|
||||
rect.0 + (rect.2 - text_width(label)) * 0.5,
|
||||
rect.1 + (rect.3 - cell_h) / 2.0,
|
||||
if editor.auth == auth_mode_for_label(label) { skin.ink_strong } else { skin.ink },
|
||||
if editor.auth == auth_mode_for_label(label) {
|
||||
skin.ink_strong
|
||||
} else {
|
||||
skin.ink_dim
|
||||
},
|
||||
label,
|
||||
glyph_cache,
|
||||
);
|
||||
@@ -456,10 +524,15 @@ impl Display {
|
||||
&size,
|
||||
cancel,
|
||||
primary,
|
||||
cancel_label,
|
||||
primary_label,
|
||||
cancel_group_x,
|
||||
primary_group_x,
|
||||
cancel_key_rect,
|
||||
primary_key_rect,
|
||||
cancel_action,
|
||||
primary_action,
|
||||
cancel_key,
|
||||
primary_key,
|
||||
cell_h,
|
||||
&text_width,
|
||||
&skin,
|
||||
);
|
||||
|
||||
@@ -501,14 +574,15 @@ fn input_quads(
|
||||
rect.3 + s(2.0),
|
||||
s(7.0),
|
||||
if active {
|
||||
accent
|
||||
} else if hovered {
|
||||
skin.hover_strong
|
||||
Rgba::new(accent.r, accent.g, accent.b, if skin.is_light { 118 } else { 136 })
|
||||
} else {
|
||||
skin.hairline
|
||||
},
|
||||
));
|
||||
quads.push(UiQuad::solid(rect.0, rect.1, rect.2, rect.3, s(6.0), skin.input));
|
||||
if hovered && !active {
|
||||
quads.push(UiQuad::solid(rect.0, rect.1, rect.2, rect.3, s(6.0), skin.hover));
|
||||
}
|
||||
}
|
||||
|
||||
fn button_quads(
|
||||
@@ -521,7 +595,8 @@ fn button_quads(
|
||||
scale: f32,
|
||||
) {
|
||||
let s = |value: f32| value * scale;
|
||||
for (rect, edge) in [(cancel, skin.hairline), (primary, accent)] {
|
||||
let primary_edge = Rgba::new(accent.r, accent.g, accent.b, if skin.is_light { 72 } else { 92 });
|
||||
for (rect, edge) in [(cancel, skin.hairline), (primary, primary_edge)] {
|
||||
quads.push(UiQuad::solid(
|
||||
rect.0 - s(1.0),
|
||||
rect.1 - s(1.0),
|
||||
@@ -531,20 +606,13 @@ fn button_quads(
|
||||
edge,
|
||||
));
|
||||
}
|
||||
quads.push(UiQuad::solid(cancel.0, cancel.1, cancel.2, cancel.3, s(8.0), skin.input));
|
||||
quads.push(UiQuad::solid(primary.0, primary.1, primary.2, primary.3, s(8.0), accent));
|
||||
quads.push(UiQuad::solid(cancel.0, cancel.1, cancel.2, cancel.3, s(8.0), skin.surface));
|
||||
quads.push(UiQuad::solid(primary.0, primary.1, primary.2, primary.3, s(8.0), skin.accent_soft));
|
||||
if hover == SshEditorHit::Cancel {
|
||||
quads.push(UiQuad::solid(cancel.0, cancel.1, cancel.2, cancel.3, s(8.0), skin.hover));
|
||||
}
|
||||
if hover == SshEditorHit::Primary {
|
||||
quads.push(UiQuad::solid(
|
||||
primary.0,
|
||||
primary.1,
|
||||
primary.2,
|
||||
primary.3,
|
||||
s(8.0),
|
||||
skin.hover_strong,
|
||||
));
|
||||
quads.push(UiQuad::solid(primary.0, primary.1, primary.2, primary.3, s(8.0), skin.hover));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,26 +729,48 @@ fn draw_button_text(
|
||||
size: &SizeInfo,
|
||||
cancel: Rect,
|
||||
primary: Rect,
|
||||
cancel_label: &str,
|
||||
primary_label: &str,
|
||||
cancel_group_x: f32,
|
||||
primary_group_x: f32,
|
||||
cancel_key_rect: Rect,
|
||||
primary_key_rect: Rect,
|
||||
cancel_action: &str,
|
||||
primary_action: &str,
|
||||
cancel_key: &str,
|
||||
primary_key: &str,
|
||||
cell_h: f32,
|
||||
text_width: &impl Fn(&str) -> f32,
|
||||
skin: &theme::Skin,
|
||||
) {
|
||||
renderer.draw_chrome_text(
|
||||
size,
|
||||
cancel.0 + (cancel.2 - text_width(cancel_label)) * 0.5,
|
||||
cancel_group_x,
|
||||
cancel.1 + (cancel.3 - cell_h) / 2.0,
|
||||
skin.ink,
|
||||
cancel_label,
|
||||
cancel_action,
|
||||
glyph_cache,
|
||||
);
|
||||
renderer.draw_chrome_text(
|
||||
size,
|
||||
primary.0 + (primary.2 - text_width(primary_label)) * 0.5,
|
||||
primary_group_x,
|
||||
primary.1 + (primary.3 - cell_h) / 2.0,
|
||||
skin.ink_on_accent,
|
||||
primary_label,
|
||||
skin.ink_strong,
|
||||
primary_action,
|
||||
glyph_cache,
|
||||
);
|
||||
renderer.draw_chrome_text(
|
||||
size,
|
||||
cancel_key_rect.0 + (cancel_key_rect.2 - size.cell_width() * cancel_key.len() as f32) * 0.5,
|
||||
cancel_key_rect.1 + (cancel_key_rect.3 - cell_h) / 2.0,
|
||||
skin.ink_dim,
|
||||
cancel_key,
|
||||
glyph_cache,
|
||||
);
|
||||
renderer.draw_chrome_text(
|
||||
size,
|
||||
primary_key_rect.0
|
||||
+ (primary_key_rect.2 - size.cell_width() * primary_key.len() as f32) * 0.5,
|
||||
primary_key_rect.1 + (primary_key_rect.3 - cell_h) / 2.0,
|
||||
skin.ink_dim,
|
||||
primary_key,
|
||||
glyph_cache,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::path::Path;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{fmt, ptr};
|
||||
use std::path::Path;
|
||||
|
||||
use ahash::RandomState;
|
||||
use crossfont::Metrics;
|
||||
@@ -22,17 +22,17 @@ use crate::display::SizeInfo;
|
||||
use crate::display::color::Rgb;
|
||||
use crate::display::content::RenderableCell;
|
||||
use crate::gl;
|
||||
use crate::renderer::image::ImageRenderer;
|
||||
use crate::renderer::rects::{RectRenderer, RenderRect};
|
||||
use crate::renderer::shader::ShaderError;
|
||||
use crate::renderer::image::ImageRenderer;
|
||||
use crate::renderer::ui::{UiQuad, UiRenderer};
|
||||
|
||||
pub mod image;
|
||||
pub mod platform;
|
||||
pub mod rects;
|
||||
pub mod ui;
|
||||
mod shader;
|
||||
mod text;
|
||||
pub mod ui;
|
||||
pub(crate) use text::Rasterizer;
|
||||
|
||||
pub use text::{GlyphCache, LoaderApi};
|
||||
@@ -302,12 +302,8 @@ impl Renderer {
|
||||
let scale_y = -2.0 / h * mult;
|
||||
|
||||
let (id, u_projection) = match &self.text_renderer {
|
||||
TextRendererProvider::Gles2(r) => {
|
||||
(r.program().id(), r.program().projection_uniform())
|
||||
},
|
||||
TextRendererProvider::Glsl3(r) => {
|
||||
(r.program().id(), r.program().projection_uniform())
|
||||
},
|
||||
TextRendererProvider::Gles2(r) => (r.program().id(), r.program().projection_uniform()),
|
||||
TextRendererProvider::Glsl3(r) => (r.program().id(), r.program().projection_uniform()),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
@@ -364,8 +360,7 @@ impl Renderer {
|
||||
if width == 0 {
|
||||
return None; // combining/zero-width marks have no cell of their own
|
||||
}
|
||||
let flags =
|
||||
if width == 2 { Flags::WIDE_CHAR | style } else { style };
|
||||
let flags = if width == 2 { Flags::WIDE_CHAR | style } else { style };
|
||||
let cell = RenderableCell {
|
||||
point: Point::new(0, Column(col)),
|
||||
character,
|
||||
@@ -453,9 +448,27 @@ impl Renderer {
|
||||
style: Flags,
|
||||
text: &str,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
) -> f32 {
|
||||
self.draw_doc_text_tracked(size_info, x, y, scale, 0.0, fg, style, text, glyph_cache)
|
||||
}
|
||||
|
||||
/// Document text with explicit physical-pixel tracking between glyphs.
|
||||
/// This is reserved for compact UI labels; terminal cells must remain fixed.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn draw_doc_text_tracked(
|
||||
&mut self,
|
||||
size_info: &SizeInfo,
|
||||
x: f32,
|
||||
y: f32,
|
||||
scale: f32,
|
||||
tracking: f32,
|
||||
fg: Rgb,
|
||||
style: Flags,
|
||||
text: &str,
|
||||
glyph_cache: &mut GlyphCache,
|
||||
) -> f32 {
|
||||
let cell_w = size_info.cell_width();
|
||||
if (scale - 1.0).abs() < 0.01 {
|
||||
if (scale - 1.0).abs() < 0.01 && tracking.abs() < 0.01 {
|
||||
self.draw_chrome_text_styled(size_info, x, y, fg, style, text, glyph_cache);
|
||||
let cols: usize = text.chars().map(|c| c.width().unwrap_or(0)).sum();
|
||||
return cols as f32 * cell_w;
|
||||
@@ -473,8 +486,7 @@ impl Renderer {
|
||||
// scaled run's baseline lands where a truly scaled cell would put it:
|
||||
// Δ = (cell_h + descent) · (scale − 1) — i.e. ascent · (scale − 1).
|
||||
let metrics = glyph_cache.font_metrics();
|
||||
let anchor_y =
|
||||
(y + (size_info.cell_height() + metrics.descent) * (scale - 1.0)).round();
|
||||
let anchor_y = (y + (size_info.cell_height() + metrics.descent) * (scale - 1.0)).round();
|
||||
|
||||
// Advance by the UNfloored design advance, scaled. The grid's
|
||||
// `cell_width` is `floor(average_advance)`; at base size the small-ppem
|
||||
@@ -502,7 +514,7 @@ impl Renderer {
|
||||
underline: fg,
|
||||
};
|
||||
self.draw_cells(size_info, glyph_cache, std::iter::once(cell));
|
||||
pen_x += width as f32 * advance;
|
||||
pen_x += width as f32 * advance + tracking;
|
||||
}
|
||||
|
||||
glyph_cache.font_size = base_size;
|
||||
@@ -797,9 +809,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn asymmetric_bottom_padding_is_the_cell_viewport_origin() {
|
||||
let size = SizeInfo::new_fully_asymmetric(
|
||||
1000.0, 1000.0, 10.0, 20.0, 260.0, 20.0, 64.0, 16.0,
|
||||
);
|
||||
let size =
|
||||
SizeInfo::new_fully_asymmetric(1000.0, 1000.0, 10.0, 20.0, 260.0, 20.0, 64.0, 16.0);
|
||||
|
||||
assert_eq!(cell_viewport(&size, 1000.0), (260, 16, 720, 920));
|
||||
// Startup calls resize before the renderer has recorded the window height.
|
||||
|
||||
Reference in new issue
Block a user