Skip to content

Commit 0f6fa8f

Browse files
committed
Add colorscheme config
1 parent 34d3b84 commit 0f6fa8f

6 files changed

Lines changed: 160 additions & 44 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ mime_guess = "^2.0.4"
4646
nom = "7.0.0"
4747
open = "3.2.0"
4848
rand = "0.8.5"
49-
ratatui = "0.29.0"
49+
ratatui = { version = "0.29.0", features = ["serde"] }
5050
ratatui-image = { version = "~8.0.1", features = ["serde"] }
5151
regex = "^1.5"
5252
rpassword = "^7.2"

‎src/config.rs‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,71 @@ pub struct ImagePreviewProtocolValues {
523523
pub font_size: Option<(u16, u16)>,
524524
}
525525

526+
#[derive(Clone, Deserialize, Default)]
527+
pub struct Colorscheme {
528+
pub border: Option<Color>,
529+
pub border_unfocused: Option<Color>,
530+
pub window_title: Option<Color>,
531+
pub tab_title: Option<Color>,
532+
pub tab_title_unfocused: Option<Color>,
533+
pub room_list: Option<Color>,
534+
pub room_list_unread: Option<Color>,
535+
}
536+
537+
#[derive(Clone, Deserialize)]
538+
pub struct ColorschemeValues {
539+
pub border: Style,
540+
pub border_unfocused: Style,
541+
pub window_title: Style,
542+
pub tab_title: Style,
543+
pub tab_title_unfocused: Style,
544+
pub room_list: Style,
545+
pub room_list_unread: Style,
546+
}
547+
548+
fn merge_colorscheme(
549+
profile: Option<Colorscheme>,
550+
global: Option<Colorscheme>,
551+
) -> Option<Colorscheme> {
552+
match (profile, global) {
553+
(None, None) => None,
554+
(Some(c), None) | (None, Some(c)) => Some(c),
555+
(Some(profile), Some(global)) => {
556+
Some(Colorscheme {
557+
border: profile.border.or(global.border),
558+
border_unfocused: profile.border_unfocused.or(global.border_unfocused),
559+
window_title: profile.window_title.or(global.window_title),
560+
tab_title: profile.tab_title.or(global.tab_title),
561+
tab_title_unfocused: profile.tab_title_unfocused.or(global.tab_title_unfocused),
562+
room_list: profile.room_list.or(global.room_list),
563+
room_list_unread: profile.room_list_unread.or(global.room_list_unread),
564+
})
565+
},
566+
}
567+
}
568+
569+
impl Colorscheme {
570+
pub fn values(self) -> ColorschemeValues {
571+
let border = self.border.map(Into::into).unwrap_or_default();
572+
let border_unfocused = self.border_unfocused.map(Into::into).unwrap_or(border);
573+
let window_title = self.window_title.map(Into::into).unwrap_or_default();
574+
let tab_title = self.tab_title.map(Into::into).unwrap_or_default();
575+
let tab_title_unfocused = self.tab_title_unfocused.map(Into::into).unwrap_or(tab_title);
576+
let room_list = self.room_list.map(Into::into).unwrap_or_default();
577+
let room_list_unread = self.room_list_unread.map(Into::into).unwrap_or(room_list);
578+
579+
ColorschemeValues {
580+
border,
581+
border_unfocused,
582+
window_title,
583+
tab_title,
584+
tab_title_unfocused,
585+
room_list,
586+
room_list_unread,
587+
}
588+
}
589+
}
590+
526591
#[derive(Clone)]
527592
pub struct SortValues {
528593
pub chats: Vec<SortColumn<SortFieldRoom>>,
@@ -577,6 +642,7 @@ pub struct TunableValues {
577642
pub user_gutter_width: usize,
578643
pub external_edit_file_suffix: String,
579644
pub tabstop: usize,
645+
pub colors: ColorschemeValues,
580646
}
581647

582648
#[derive(Clone, Default, Deserialize)]
@@ -604,6 +670,7 @@ pub struct Tunables {
604670
pub user_gutter_width: Option<usize>,
605671
pub external_edit_file_suffix: Option<String>,
606672
pub tabstop: Option<usize>,
673+
pub colors: Option<Colorscheme>,
607674
}
608675

609676
impl Tunables {
@@ -637,6 +704,7 @@ impl Tunables {
637704
.external_edit_file_suffix
638705
.or(other.external_edit_file_suffix),
639706
tabstop: self.tabstop.or(other.tabstop),
707+
colors: merge_colorscheme(self.colors, other.colors),
640708
}
641709
}
642710

@@ -666,6 +734,7 @@ impl Tunables {
666734
.external_edit_file_suffix
667735
.unwrap_or_else(|| ".md".to_string()),
668736
tabstop: self.tabstop.unwrap_or(4),
737+
colors: self.colors.unwrap_or_default().values(),
669738
}
670739
}
671740
}

‎src/main.rs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ impl Application {
304304
let focused = self.focused;
305305
let sstate = &mut self.screen;
306306
let term = &mut self.terminal;
307+
let colors = store.application.settings.tunables.colors.clone();
307308

308309
if store.application.ring_bell {
309310
store.application.ring_bell = term.backend_mut().write_all(&[7]).is_err();
@@ -328,9 +329,10 @@ impl Application {
328329
.show_dialog(dialogstr)
329330
.show_mode(modestr)
330331
.borders(true)
331-
.border_style(Style::default().add_modifier(Modifier::DIM))
332-
.tab_style(Style::default().add_modifier(Modifier::DIM))
333-
.tab_style_focused(Style::default().remove_modifier(Modifier::DIM))
332+
.border_style(colors.border_unfocused.add_modifier(Modifier::DIM))
333+
.border_style_focused(colors.border.remove_modifier(Modifier::DIM))
334+
.tab_style(colors.tab_title_unfocused.add_modifier(Modifier::DIM))
335+
.tab_style_focused(colors.tab_title.remove_modifier(Modifier::DIM))
334336
.focus(focused);
335337
f.render_stateful_widget(screen, area, sstate);
336338

‎src/tests.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::{
2626
user_color,
2727
user_style_from_color,
2828
ApplicationSettings,
29+
Colorscheme,
2930
DirectoryValues,
3031
Notifications,
3132
NotifyVia,
@@ -199,6 +200,7 @@ pub fn mock_tunables() -> TunableValues {
199200
image_preview: None,
200201
user_gutter_width: 30,
201202
tabstop: 4,
203+
colors: Colorscheme::default().values(),
202204
}
203205
}
204206

‎src/windows/mod.rs‎

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -92,37 +92,37 @@ type MatrixRoomInfo = Arc<(MatrixRoom, Option<Tags>)>;
9292
const MEMBER_FETCH_DEBOUNCE: Duration = Duration::from_secs(5);
9393

9494
#[inline]
95-
fn bold_style() -> Style {
96-
Style::default().add_modifier(StyleModifier::BOLD)
95+
fn bold_style(style: Style) -> Style {
96+
style.add_modifier(StyleModifier::BOLD)
9797
}
9898

9999
#[inline]
100-
fn bold_span(s: &str) -> Span {
101-
Span::styled(s, bold_style())
100+
fn bold_span(s: &str, style: Style) -> Span {
101+
Span::styled(s, bold_style(style))
102102
}
103103

104104
#[inline]
105-
fn bold_spans(s: &str) -> Line {
106-
bold_span(s).into()
105+
fn bold_spans(s: &str, style: Style) -> Line {
106+
bold_span(s, style).into()
107107
}
108108

109109
#[inline]
110-
fn selected_style(selected: bool) -> Style {
110+
fn selected_style(selected: bool, style: Style) -> Style {
111111
if selected {
112-
Style::default().add_modifier(StyleModifier::REVERSED)
112+
style.add_modifier(StyleModifier::REVERSED)
113113
} else {
114-
Style::default()
114+
style
115115
}
116116
}
117117

118118
#[inline]
119-
fn selected_span(s: &str, selected: bool) -> Span {
120-
Span::styled(s, selected_style(selected))
119+
fn selected_span(s: &str, selected: bool, style: Style) -> Span {
120+
Span::styled(s, selected_style(selected, style))
121121
}
122122

123123
#[inline]
124-
fn selected_text(s: &str, selected: bool) -> Text {
125-
Text::from(selected_span(s, selected))
124+
fn selected_text(s: &str, selected: bool, style: Style) -> Text {
125+
Text::from(selected_span(s, selected, style))
126126
}
127127

128128
fn name_and_labels(name: &str, unread: bool, style: Style) -> (Span<'_>, Vec<Vec<Span<'_>>>) {
@@ -744,14 +744,15 @@ impl Window<IambInfo> for IambWindow {
744744
}
745745

746746
fn get_tab_title(&self, store: &mut ProgramStore) -> Line {
747+
let style = Default::default();
747748
match self {
748-
IambWindow::DirectList(_) => bold_spans("Direct Messages"),
749-
IambWindow::RoomList(_) => bold_spans("Rooms"),
750-
IambWindow::SpaceList(_) => bold_spans("Spaces"),
751-
IambWindow::VerifyList(_) => bold_spans("Verifications"),
752-
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
753-
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
754-
IambWindow::UnreadList(_) => bold_spans("Unread Messages"),
749+
IambWindow::DirectList(_) => bold_spans("Direct Messages", style),
750+
IambWindow::RoomList(_) => bold_spans("Rooms", style),
751+
IambWindow::SpaceList(_) => bold_spans("Spaces", style),
752+
IambWindow::VerifyList(_) => bold_spans("Verifications", style),
753+
IambWindow::Welcome(_) => bold_spans("Welcome to iamb", style),
754+
IambWindow::ChatList(_) => bold_spans("DMs & Rooms", style),
755+
IambWindow::UnreadList(_) => bold_spans("Unread Messages", style),
755756

756757
IambWindow::Room(w) => {
757758
let title = store.application.get_room_title(w.id());
@@ -762,8 +763,8 @@ impl Window<IambInfo> for IambWindow {
762763
let title = store.application.get_room_title(room_id.as_ref());
763764
let n = state.len();
764765
let v = vec![
765-
bold_span("Room Members "),
766-
Span::styled(format!("({n}): "), bold_style()),
766+
bold_span("Room Members ", style),
767+
Span::styled(format!("({n}): "), bold_style(style)),
767768
title.into(),
768769
];
769770
Line::from(v)
@@ -772,22 +773,23 @@ impl Window<IambInfo> for IambWindow {
772773
}
773774

774775
fn get_win_title(&self, store: &mut ProgramStore) -> Line {
776+
let style = store.application.settings.tunables.colors.window_title;
775777
match self {
776-
IambWindow::DirectList(_) => bold_spans("Direct Messages"),
777-
IambWindow::RoomList(_) => bold_spans("Rooms"),
778-
IambWindow::SpaceList(_) => bold_spans("Spaces"),
779-
IambWindow::VerifyList(_) => bold_spans("Verifications"),
780-
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
781-
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
782-
IambWindow::UnreadList(_) => bold_spans("Unread Messages"),
778+
IambWindow::DirectList(_) => bold_spans("Direct Messages", style),
779+
IambWindow::RoomList(_) => bold_spans("Rooms", style),
780+
IambWindow::SpaceList(_) => bold_spans("Spaces", style),
781+
IambWindow::VerifyList(_) => bold_spans("Verifications", style),
782+
IambWindow::Welcome(_) => bold_spans("Welcome to iamb", style),
783+
IambWindow::ChatList(_) => bold_spans("DMs & Rooms", style),
784+
IambWindow::UnreadList(_) => bold_spans("Unread Messages", style),
783785

784786
IambWindow::Room(w) => w.get_title(store),
785787
IambWindow::MemberList(state, room_id, _) => {
786788
let title = store.application.get_room_title(room_id.as_ref());
787789
let n = state.len();
788790
let v = vec![
789-
bold_span("Room Members "),
790-
Span::styled(format!("({n}): "), bold_style()),
791+
bold_span("Room Members ", style),
792+
Span::styled(format!("({n}): "), bold_style(style)),
791793
title.into(),
792794
];
793795
Line::from(v)
@@ -959,9 +961,19 @@ impl Display for GenericChatItem {
959961
}
960962

961963
impl ListItem<IambInfo> for GenericChatItem {
962-
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
964+
fn show(
965+
&self,
966+
selected: bool,
967+
_: &ViewportContext<ListCursor>,
968+
store: &mut ProgramStore,
969+
) -> Text {
963970
let unread = self.unread.is_unread();
964-
let style = selected_style(selected);
971+
let style = if unread {
972+
store.application.settings.tunables.colors.room_list_unread
973+
} else {
974+
store.application.settings.tunables.colors.room_list
975+
};
976+
let style = selected_style(selected, style);
965977
let (name, mut labels) = name_and_labels(&self.name, unread, style);
966978
let mut spans = vec![name];
967979

@@ -1073,9 +1085,19 @@ impl Display for RoomItem {
10731085
}
10741086

10751087
impl ListItem<IambInfo> for RoomItem {
1076-
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
1088+
fn show(
1089+
&self,
1090+
selected: bool,
1091+
_: &ViewportContext<ListCursor>,
1092+
store: &mut ProgramStore,
1093+
) -> Text {
10771094
let unread = self.unread.is_unread();
1078-
let style = selected_style(selected);
1095+
let style = if unread {
1096+
store.application.settings.tunables.colors.room_list_unread
1097+
} else {
1098+
store.application.settings.tunables.colors.room_list
1099+
};
1100+
let style = selected_style(selected, style);
10791101
let (name, mut labels) = name_and_labels(&self.name, unread, style);
10801102
let mut spans = vec![name];
10811103

@@ -1177,9 +1199,19 @@ impl Display for DirectItem {
11771199
}
11781200

11791201
impl ListItem<IambInfo> for DirectItem {
1180-
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
1202+
fn show(
1203+
&self,
1204+
selected: bool,
1205+
_: &ViewportContext<ListCursor>,
1206+
store: &mut ProgramStore,
1207+
) -> Text {
11811208
let unread = self.unread.is_unread();
1182-
let style = selected_style(selected);
1209+
let style = if unread {
1210+
store.application.settings.tunables.colors.room_list_unread
1211+
} else {
1212+
store.application.settings.tunables.colors.room_list
1213+
};
1214+
let style = selected_style(selected, style);
11831215
let (name, mut labels) = name_and_labels(&self.name, unread, style);
11841216
let mut spans = vec![name];
11851217

@@ -1280,8 +1312,17 @@ impl Display for SpaceItem {
12801312
}
12811313

12821314
impl ListItem<IambInfo> for SpaceItem {
1283-
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
1284-
selected_text(self.name.as_str(), selected)
1315+
fn show(
1316+
&self,
1317+
selected: bool,
1318+
_: &ViewportContext<ListCursor>,
1319+
store: &mut ProgramStore,
1320+
) -> Text {
1321+
selected_text(
1322+
self.name.as_str(),
1323+
selected,
1324+
store.application.settings.tunables.colors.room_list,
1325+
)
12851326
}
12861327

12871328
fn get_word(&self) -> Option<String> {
@@ -1415,7 +1456,7 @@ impl ListItem<IambInfo> for VerifyItem {
14151456
let mut lines = vec![];
14161457

14171458
let bold = Style::default().add_modifier(StyleModifier::BOLD);
1418-
let item = Span::styled(self.show_item(), selected_style(selected));
1459+
let item = Span::styled(self.show_item(), selected_style(selected, Default::default()));
14191460
lines.push(Line::from(item));
14201461

14211462
if self.sasv1.is_done() {

0 commit comments

Comments
 (0)