Normalize scrollbar style with figma designs

This commit is contained in:
Jeremy Soller 2024-01-11 11:33:39 -07:00
parent 0eea366410
commit 3f09e17156
No known key found for this signature in database
GPG key ID: DCFCA852D3906975
4 changed files with 153 additions and 72 deletions

View file

@ -4,11 +4,11 @@ use cosmic::{
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry}, cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
theme, theme,
}; };
use cosmic_text::{Metrics, Weight, Stretch}; use cosmic_text::{Metrics, Stretch, Weight};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::OnceLock;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::OnceLock;
pub const CONFIG_VERSION: u64 = 1; pub const CONFIG_VERSION: u64 = 1;
@ -99,7 +99,7 @@ impl Config {
static NUM_TO_TYPED_MAP: OnceLock<BTreeMap<u16, Stretch>> = OnceLock::new(); static NUM_TO_TYPED_MAP: OnceLock<BTreeMap<u16, Stretch>> = OnceLock::new();
NUM_TO_TYPED_MAP.get_or_init(|| { NUM_TO_TYPED_MAP.get_or_init(|| {
populate_num_typed_map!{ populate_num_typed_map! {
UltraCondensed, ExtraCondensed, Condensed, SemiCondensed, UltraCondensed, ExtraCondensed, Condensed, SemiCondensed,
Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded, Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded,
} }

View file

@ -14,14 +14,20 @@ use cosmic::{
futures::SinkExt, futures::SinkExt,
keyboard::{Event as KeyEvent, KeyCode, Modifiers}, keyboard::{Event as KeyEvent, KeyCode, Modifiers},
subscription::{self, Subscription}, subscription::{self, Subscription},
window, Event, Length, Point, Padding, window, Event, Length, Padding, Point,
}, },
style, style,
widget::{self, segmented_button}, widget::{self, segmented_button},
Application, ApplicationExt, Element, Application, ApplicationExt, Element,
}; };
use cosmic_text::{Family, Weight, Stretch, fontdb::FaceInfo}; use cosmic_text::{fontdb::FaceInfo, Family, Stretch, Weight};
use std::{any::TypeId, collections::{HashMap, BTreeMap, BTreeSet}, env, process, sync::Mutex, time::Duration}; use std::{
any::TypeId,
collections::{BTreeMap, BTreeSet, HashMap},
env, process,
sync::Mutex,
time::Duration,
};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use config::{AppTheme, Config, CONFIG_VERSION}; use config::{AppTheme, Config, CONFIG_VERSION};
@ -267,34 +273,43 @@ impl App {
.into_iter() .into_iter()
.collect(); .collect();
self.curr_font_stretch_names = self.curr_font_stretches.iter() self.curr_font_stretch_names = self
.curr_font_stretches
.iter()
.map(|stretch| &self.all_font_stretches_vals_names_map[stretch]) .map(|stretch| &self.all_font_stretches_vals_names_map[stretch])
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !self.curr_font_stretches.contains(&self.config.typed_font_stretch()) { if !self
.curr_font_stretches
.contains(&self.config.typed_font_stretch())
{
self.config.font_stretch = Stretch::Normal.to_number(); self.config.font_stretch = Stretch::Normal.to_number();
} }
let curr_weights = |conf_stretch| curr_font_faces let curr_weights = |conf_stretch| {
.iter() curr_font_faces
.filter(|face| face.stretch == conf_stretch) .iter()
.map(|face| face.weight.0) .filter(|face| face.stretch == conf_stretch)
.collect::<BTreeSet<_>>() // remove duplicates and sort .map(|face| face.weight.0)
.into_iter() .collect::<BTreeSet<_>>() // remove duplicates and sort
.collect(); .into_iter()
.collect()
};
self.curr_font_weights = curr_weights(self.config.typed_font_stretch()); self.curr_font_weights = curr_weights(self.config.typed_font_stretch());
if self.curr_font_weights.is_empty() { if self.curr_font_weights.is_empty() {
// stretch fallback // stretch fallback
self.config.font_stretch = Stretch::Normal.to_number(); self.config.font_stretch = Stretch::Normal.to_number();
} }
self.curr_font_weights = curr_weights(self.config.typed_font_stretch()); self.curr_font_weights = curr_weights(self.config.typed_font_stretch());
assert!(!self.curr_font_weights.is_empty()); assert!(!self.curr_font_weights.is_empty());
self.curr_font_weight_names = self.curr_font_weights.iter() self.curr_font_weight_names = self
.curr_font_weights
.iter()
.map(|weight| &self.all_font_weights_vals_names_map[weight]) .map(|weight| &self.all_font_weights_vals_names_map[weight])
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -303,7 +318,10 @@ impl App {
self.config.font_weight = Weight::NORMAL.0; self.config.font_weight = Weight::NORMAL.0;
} }
if !self.curr_font_weights.contains(&self.config.bold_font_weight) { if !self
.curr_font_weights
.contains(&self.config.bold_font_weight)
{
self.config.bold_font_weight = Weight::BOLD.0; self.config.bold_font_weight = Weight::BOLD.0;
} }
} }
@ -354,23 +372,29 @@ impl App {
let section = widget::settings::view_section("") let section = widget::settings::view_section("")
.add( .add(
widget::settings::item::builder(fl!("default-font-stretch")).control( widget::settings::item::builder(fl!("default-font-stretch")).control(
widget::dropdown(&self.curr_font_stretch_names, font_stretch_selected, |index| { widget::dropdown(
Message::DefaultFontStretch(index) &self.curr_font_stretch_names,
}), font_stretch_selected,
|index| Message::DefaultFontStretch(index),
),
), ),
) )
.add( .add(
widget::settings::item::builder(fl!("default-font-weight")).control( widget::settings::item::builder(fl!("default-font-weight")).control(
widget::dropdown(&self.curr_font_weight_names, font_weight_selected, |index| { widget::dropdown(
Message::DefaultFontWeight(index) &self.curr_font_weight_names,
}), font_weight_selected,
|index| Message::DefaultFontWeight(index),
),
), ),
) )
.add( .add(
widget::settings::item::builder(fl!("default-bold-font-weight")).control( widget::settings::item::builder(fl!("default-bold-font-weight")).control(
widget::dropdown(&self.curr_font_weight_names, bold_font_weight_selected, |index| { widget::dropdown(
Message::DefaultBoldFontWeight(index) &self.curr_font_weight_names,
}), bold_font_weight_selected,
|index| Message::DefaultBoldFontWeight(index),
),
), ),
); );
let padding = Padding { let padding = Padding {
@ -418,8 +442,10 @@ impl App {
)), )),
) )
.add( .add(
widget::settings::item::builder(fl!("advanced-font-settings")) widget::settings::item::builder(fl!("advanced-font-settings")).toggler(
.toggler(self.show_advanced_font_settings, Message::ShowAdvancedFontSettings), self.show_advanced_font_settings,
Message::ShowAdvancedFontSettings,
),
); );
if self.show_advanced_font_settings { if self.show_advanced_font_settings {
@ -500,13 +526,16 @@ impl Application for App {
for face in font_system.raw().db().faces() { for face in font_system.raw().db().faces() {
// only monospace fonts and weights that match named constants. // only monospace fonts and weights that match named constants.
let weight = face.weight.0; let weight = face.weight.0;
if face.monospaced && {1..9}.contains(&{weight / 100}) && weight % 100 == 0 { if face.monospaced && { 1..9 }.contains(&{ weight / 100 }) && weight % 100 == 0 {
//TODO: get localized name if possible //TODO: get localized name if possible
let font_name = face let font_name = face
.families .families
.get(0) .get(0)
.map_or_else(|| face.post_script_name.to_string(), |x| x.0.to_string()); .map_or_else(|| face.post_script_name.to_string(), |x| x.0.to_string());
font_name_faces_map.entry(font_name).or_default().push(face.clone()); font_name_faces_map
.entry(font_name)
.or_default()
.push(face.clone());
} }
} }
@ -514,8 +543,12 @@ impl Application for App {
// a `Stretch::Normal` face. // a `Stretch::Normal` face.
// This is important for fallbacks. // This is important for fallbacks.
font_name_faces_map.retain(|_, v| { font_name_faces_map.retain(|_, v| {
let has_normal = v.iter().any(|face| face.weight == Weight::NORMAL && face.stretch == Stretch::Normal); let has_normal = v
let has_bold = v.iter().any(|face| face.weight == Weight::BOLD && face.stretch == Stretch::Normal); .iter()
.any(|face| face.weight == Weight::NORMAL && face.stretch == Stretch::Normal);
let has_bold = v
.iter()
.any(|face| face.weight == Weight::BOLD && face.stretch == Stretch::Normal);
has_normal && has_bold has_normal && has_bold
}); });
font_name_faces_map font_name_faces_map
@ -543,12 +576,12 @@ impl Application for App {
}; };
} }
populate_font_weights!{ populate_font_weights! {
THIN, EXTRA_LIGHT, LIGHT, NORMAL, MEDIUM, THIN, EXTRA_LIGHT, LIGHT, NORMAL, MEDIUM,
SEMIBOLD, BOLD, EXTRA_BOLD, BLACK, SEMIBOLD, BOLD, EXTRA_BOLD, BLACK,
}; };
let mut all_font_stretches_vals_names_map= BTreeMap::new(); let mut all_font_stretches_vals_names_map = BTreeMap::new();
macro_rules! populate_font_stretches { macro_rules! populate_font_stretches {
($($stretch:ident,)+) => { ($($stretch:ident,)+) => {
@ -560,7 +593,7 @@ impl Application for App {
}; };
} }
populate_font_stretches!{ populate_font_stretches! {
UltraCondensed, ExtraCondensed, Condensed, SemiCondensed, UltraCondensed, ExtraCondensed, Condensed, SemiCondensed,
Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded, Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded,
}; };
@ -907,7 +940,7 @@ impl Application for App {
} }
Message::ShowAdvancedFontSettings(show) => { Message::ShowAdvancedFontSettings(show) => {
self.show_advanced_font_settings = show; self.show_advanced_font_settings = show;
}, }
Message::WindowClose => { Message::WindowClose => {
return window::close(window::Id::MAIN); return window::close(window::Id::MAIN);
} }
@ -1094,7 +1127,6 @@ impl Application for App {
let (event_tx, mut event_rx) = mpsc::channel(100); let (event_tx, mut event_rx) = mpsc::channel(100);
output.send(Message::TermEventTx(event_tx)).await.unwrap(); output.send(Message::TermEventTx(event_tx)).await.unwrap();
// Avoid creating two tabs at startup // Avoid creating two tabs at startup
tokio::time::sleep(Duration::from_millis(50)).await; tokio::time::sleep(Duration::from_millis(50)).await;

View file

@ -16,7 +16,7 @@ use alacritty_terminal::{
}; };
use cosmic::{iced::advanced::graphics::text::font_system, widget::segmented_button}; use cosmic::{iced::advanced::graphics::text::font_system, widget::segmented_button};
use cosmic_text::{ use cosmic_text::{
Attrs, AttrsList, Buffer, BufferLine, CacheKeyFlags, Family, Metrics, Shaping, Weight, Stretch, Wrap, Attrs, AttrsList, Buffer, BufferLine, CacheKeyFlags, Family, Metrics, Shaping, Weight, Wrap,
}; };
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -349,16 +349,15 @@ impl Terminal {
let mut update = false; let mut update = false;
if self.default_attrs.stretch != config.typed_font_stretch() { if self.default_attrs.stretch != config.typed_font_stretch() {
self.default_attrs = self.default_attrs.stretch(config.typed_font_stretch()); self.default_attrs = self.default_attrs.stretch(config.typed_font_stretch());
update_cell_size = true; update_cell_size = true;
} }
if self.default_attrs.weight.0 != config.font_weight { if self.default_attrs.weight.0 != config.font_weight {
self.default_attrs = self.default_attrs.weight(Weight(config.font_weight)); self.default_attrs = self.default_attrs.weight(Weight(config.font_weight));
update_cell_size = true; update_cell_size = true;
} }
if self.bold_font_weight.0 != config.font_weight { if self.bold_font_weight.0 != config.font_weight {
self.bold_font_weight = Weight(config.bold_font_weight); self.bold_font_weight = Weight(config.bold_font_weight);
update_cell_size = true; update_cell_size = true;
@ -437,7 +436,7 @@ impl Terminal {
// This will be added to the beginning of lines to force the shaper to treat detected RTL // This will be added to the beginning of lines to force the shaper to treat detected RTL
// lines as LTR. RTL text would still be rendered correctly. But this fixes the wrong // lines as LTR. RTL text would still be rendered correctly. But this fixes the wrong
// behavior of it being aligned to the right. // behavior of it being aligned to the right.
const LRI:char = '\u{2066}'; const LRI: char = '\u{2066}';
let instant = Instant::now(); let instant = Instant::now();
@ -492,11 +491,12 @@ impl Terminal {
let mut attrs = self.default_attrs; let mut attrs = self.default_attrs;
let cell_fg = if self.use_bright_bold && indexed.cell.flags.contains(Flags::BOLD) { let cell_fg =
as_bright(indexed.cell.fg) if self.use_bright_bold && indexed.cell.flags.contains(Flags::BOLD) {
} else { as_bright(indexed.cell.fg)
indexed.cell.fg } else {
}; indexed.cell.fg
};
let (mut fg, mut bg) = if indexed.cell.flags.contains(Flags::INVERSE) { let (mut fg, mut bg) = if indexed.cell.flags.contains(Flags::INVERSE) {
( (

View file

@ -6,6 +6,7 @@ use alacritty_terminal::{
term::TermMode, term::TermMode,
}; };
use cosmic::{ use cosmic::{
cosmic_theme::palette::{blend::Compose, WithAlpha},
iced::{ iced::{
advanced::graphics::text::{font_system, Raw}, advanced::graphics::text::{font_system, Raw},
event::{Event, Status}, event::{Event, Status},
@ -15,13 +16,14 @@ use cosmic::{
}, },
iced_core::{ iced_core::{
clipboard::Clipboard, clipboard::Clipboard,
image,
layout::{self, Layout}, layout::{self, Layout},
renderer::{self, Quad}, renderer::{self, Quad, Renderer as _},
text, text::Renderer as _,
widget::{self, tree, Widget}, widget::{self, tree, Widget},
Shell, Shell,
}, },
theme::Theme,
Renderer,
}; };
use cosmic_text::LayoutGlyph; use cosmic_text::LayoutGlyph;
use std::{ use std::{
@ -86,11 +88,9 @@ where
TerminalBox::new(terminal) TerminalBox::new(terminal)
} }
impl<'a, Message, Renderer> Widget<Message, Renderer> for TerminalBox<'a, Message> impl<'a, Message> Widget<Message, Renderer> for TerminalBox<'a, Message>
where where
Message: Clone, Message: Clone,
Renderer:
renderer::Renderer + image::Renderer<Handle = image::Handle> + text::Renderer<Raw = Raw>,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>() tree::Tag::of::<State>()
@ -182,18 +182,18 @@ where
&self, &self,
tree: &widget::Tree, tree: &widget::Tree,
renderer: &mut Renderer, renderer: &mut Renderer,
_theme: &Renderer::Theme, theme: &Theme,
_style: &renderer::Style, _style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
let instant = Instant::now(); let instant = Instant::now();
let state = tree.state.downcast_ref::<State>(); let state = tree.state.downcast_ref::<State>();
//TODO: make this configurable let cosmic_theme = theme.cosmic();
let scrollbar_w = 8.0; let scrollbar_w = cosmic_theme.spacing.space_xxs as f32;
let view_position = let view_position =
layout.position() + [self.padding.left as f32, self.padding.top as f32].into(); layout.position() + [self.padding.left as f32, self.padding.top as f32].into();
@ -334,19 +334,68 @@ where
[view_w as f32, scrollbar_y].into(), [view_w as f32, scrollbar_y].into(),
Size::new(scrollbar_w, scrollbar_h), Size::new(scrollbar_w, scrollbar_h),
); );
let scrollbar_alpha = match &state.dragging {
Some(Dragging::Scrollbar { .. }) => 0.5, let pressed = match &state.dragging {
_ => 0.25, Some(Dragging::Scrollbar { .. }) => true,
_ => false,
}; };
let mut hover = false;
if let Some(p) = cursor_position.position_in(layout.bounds()) {
let x = p.x - self.padding.left;
let y = p.y - self.padding.top;
if x >= scrollbar_rect.x && x < (scrollbar_rect.x + scrollbar_rect.width) {
hover = true;
}
}
let mut scrollbar_draw = scrollbar_rect + Vector::new(view_position.x, view_position.y);
if !hover && !pressed {
// Decrease draw width and keep centered when not hovered or pressed
scrollbar_draw.width /= 2.0;
scrollbar_draw.x += scrollbar_draw.width / 2.0;
}
// neutral_6, 0.7
let base_color = cosmic_theme
.palette
.neutral_6
.without_alpha()
.with_alpha(0.7);
let scrollbar_color: Color = if pressed {
// pressed_state_color, 0.5
cosmic_theme
.background
.component
.pressed
.without_alpha()
.with_alpha(0.5)
.over(base_color)
.into()
} else if hover {
// hover_state_color, 0.2
cosmic_theme
.background
.component
.hover
.without_alpha()
.with_alpha(0.2)
.over(base_color)
.into()
} else {
base_color.into()
};
renderer.fill_quad( renderer.fill_quad(
Quad { Quad {
bounds: scrollbar_rect + Vector::new(view_position.x, view_position.y), bounds: scrollbar_draw,
border_radius: (scrollbar_w / 2.0).into(), border_radius: (scrollbar_draw.width / 2.0).into(),
border_width: 0.0, border_width: 0.0,
border_color: Color::TRANSPARENT, border_color: Color::TRANSPARENT,
}, },
Color::new(1.0, 1.0, 1.0, scrollbar_alpha), scrollbar_color,
); );
state.scrollbar_rect.set(scrollbar_rect); state.scrollbar_rect.set(scrollbar_rect);
} else { } else {
state.scrollbar_rect.set(Rectangle::default()) state.scrollbar_rect.set(Rectangle::default())
@ -728,14 +777,16 @@ where
} else if x >= scrollbar_rect.x } else if x >= scrollbar_rect.x
&& x < (scrollbar_rect.x + scrollbar_rect.width) && x < (scrollbar_rect.x + scrollbar_rect.width)
{ {
if let Some(start_scroll) = terminal.scrollbar() { if terminal.scrollbar().is_some() {
let scroll_ratio = let scroll_ratio =
terminal.with_buffer(|buffer| y / buffer.size().1); terminal.with_buffer(|buffer| y / buffer.size().1);
terminal.scroll_to(scroll_ratio); terminal.scroll_to(scroll_ratio);
state.dragging = Some(Dragging::Scrollbar { if let Some(start_scroll) = terminal.scrollbar() {
start_y: y, state.dragging = Some(Dragging::Scrollbar {
start_scroll, start_y: y,
}); start_scroll,
});
}
} }
} }
} }
@ -838,11 +889,9 @@ where
} }
} }
impl<'a, Message, Renderer> From<TerminalBox<'a, Message>> for Element<'a, Message, Renderer> impl<'a, Message> From<TerminalBox<'a, Message>> for Element<'a, Message, Renderer>
where where
Message: Clone + 'a, Message: Clone + 'a,
Renderer:
renderer::Renderer + image::Renderer<Handle = image::Handle> + text::Renderer<Raw = Raw>,
{ {
fn from(terminal_box: TerminalBox<'a, Message>) -> Self { fn from(terminal_box: TerminalBox<'a, Message>) -> Self {
Self::new(terminal_box) Self::new(terminal_box)