Skip to content

Commit

Permalink
Merge pull request #521 from AmmarAbouZor/custom_themes
Browse files Browse the repository at this point in the history
Added: Custom themes on user level & Styles improvements
  • Loading branch information
AmmarAbouZor authored Jan 6, 2025
2 parents bd9143c + 8b41152 commit ceba728
Show file tree
Hide file tree
Showing 25 changed files with 1,274 additions and 262 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ rayon = "1.10"
fuzzy-matcher = "0.3"
path-absolutize = "3.1"
tui-textarea = "0.7"
ratatui = { version = "0.29", features = ["all-widgets"]}
ratatui = { version = "0.29", features = ["all-widgets", "serde"]}
arboard = { version = "3.4", default-features = false, features = ["wayland-data-control"]}

[features]
Expand Down
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ TUI-Journal is a terminal-based application written in Rust that allows you to w
- [Alpine Linux](#alpine-linux)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
- [Build & Install via Cargo ](#build--install-via-cargo)
- [Nix](#nix)
- [Homebrew](@homebrew)
- [Build & Install via Cargo](#build--install-via-cargo)
- [Usage](#usage)
- [Configuration](#configuration)
- [Themes](#themes)
- [Documentation](#documentation)
- [Acknowledgments](#acknowledgments)
- [Contributing](#contributing)
Expand Down Expand Up @@ -88,10 +91,10 @@ TUI-Journal is a terminal-based application written in Rust that allows you to w
#### Application:
- [x] Edit journals content with external text editor from within the app.
- [x] Filter & Search functionalities.
- [ ] Add mouse support
- [x] Customize themes.
- [ ] Preview mode for journals supporting Mark Down highlighting and word wrapping.
- [ ] Add mouse support
- [ ] Improve app input and rending cycle using app events to support real concurrency within the app.
- [ ] Customize themes and keybindings.

## Installation
Expand Down Expand Up @@ -216,10 +219,11 @@ $ tjournal --help
Usage: tjournal [OPTIONS] [COMMAND]
Commands:
print-config Print the current settings including the paths for the backend files [aliases: pc]
print-config Print the current settings including the paths for the backend files [aliases: pc]
import-journals Import journals from the given transfer JSON file to the current back-end file [aliases: imj]
assign-priority Assign priority for all the entires with empty priority field [aliases: ap]
help Print this message or the help of the given subcommand(s)
theme Provides commands regarding changing themes and styles of the app [aliases: style]
help Print this message or the help of the given subcommand(s)
Options:
-j, --json-file-path <FILE PATH> Sets the entries Json file path and starts using it
Expand Down Expand Up @@ -289,6 +293,10 @@ file_path = "<Documents-folder>/tui-journal/entries.json"
file_path = "<Documents-folder>/tui-journal/entries.db"
```

## Themes

Please refer to the [Themes Page](THEMES.md) for a detailed guide on customizing colors and styles within the app.

## Documentation

For detailed information about the TUI Journal app, including usage guide, keymaps, and configuration details, please refer to the [Wiki](https://github.com/AmmarAbouZor/tui-journal/wiki).
Expand Down
128 changes: 128 additions & 0 deletions THEMES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# TUI-Journal Custom Themes:

It's possible to override the styles used in the app or parts of them. Custom themes will be read from the file `theme.toml` in the configuration directory within the `tui-journal` directory.

## Table of Contents

- [Getting Started](#getting-started)
- [Themes structures](#themes-structures)
- [Themes Groups](#themes-groups)
- [Themes Types](#themes-types)
- [Color Type](#color-type)
- [Style Type](#style-type)
- [Example](#example)

## Getting started:

To get started, users can use the provided CLI subcommands under `tjournal theme`. These include specifying the path for the themes file, printing the default themes to be used as a base, or writing the themes directly into the themes file.

```
Provides commands regarding changing themes and styles of the app
Usage: tjournal theme <COMMAND>
Commands:
print-path Prints the path to the user themes file [aliases: path]
print-default Dumps the styles with the default values to be used as a reference and base for user custom themes [aliases: default]
write-defaults Creates user custom themes file if doesn't exist then writes the default styles to it [aliases: write]
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
```

## Themes structures:

### Themes Groups:

Themes are divided into the following groups:
- **general**: Styles for general controls in the app, including journal pop-ups, filter and sorting pop-ups, and more.
- **journals_list**: Styles for the main list of journals. These styles are differentiated from the general ones since they are more important and contain more information than general list items.
- **editor**: Styles for the built-in editor.
- **msgbox**: Colors for message-box prompts (Questions, Errors, Warnings, etc.).

### Themes Types:

The main types used to define themes are `Color` and `Style`.

#### Color Type

Represents the color value of an item or field. It can be defined as a terminal color, such as `Black` or `Reset`, or as an RGB value in hexadecimal format `#RRGGBB`.

#### Style Type

Represents a complete style definition with the following fields:
- **fg**: Foreground color.
- **bg**: Background color.
- **modifiers**: Modifiers change the way a piece of text is displayed. They are bitflags, so they can be easily combined.
The available modifiers are: `BOLD | DIM | ITALIC | UNDERLINED | SLOW_BLINK | RAPID_BLINK | REVERSED | HIDDEN | CROSSED_OUT`.
- **underline_color**: The color of the underline parts if the `UNDERLINED` modifier is active.

Here is an example of a style with all elements defined:

```toml
# Example of a style with all possible elements
[example_style]
fg = "#0AFA96" # Foreground Color. Colors can be in hex-decimal format "#RRGGBB"
bg = "Black" # Background Color. Also it can be one of terminal colors.
# Modifiers with all available flags. Flags can be combined as in example.
modifiers = "BOLD | DIM | ITALIC | UNDERLINED | SLOW_BLINK | RAPID_BLINK | REVERSED | HIDDEN | CROSSED_OUT"
underline_color = "Magenta" # Color for underline element if activated
```
It's worth mentioning that not all fields must be defined. Missing parts will be filled with their default values.

## Example:

Here is a small example of overriding some of the themes. For a full list of all available style fields, please use the CLI subcommands to print the default themes.

```toml
[general]
list_item_selected = { fg = "LightYellow", modifiers = "BOLD" }
input_block_active = { fg = "Blue" }

[general.input_block_invalid]
fg = "Red"
modifiers = "BOLD | SLOW_BLINK"

[general.input_corsur_active]
fg = "Black"
bg = "LightYellow"
modifiers = "RAPID_BLINK"

[general.list_highlight_active]
fg = "Black"
bg = "LightGreen"
modifiers = "UNDERLINED"
underline_color = "Magenta"

[journals_list.block_inactive]
fg = "Grey"
modifiers = ""

[journals_list.highlight_active]
fg = "Red"
bg = "LightGreen"
modifiers = "BOLD | ITALIC"

[journals_list.highlight_inactive]
fg = "Grey"
bg = "LightBlue"
modifiers = "BOLD"

[journals_list.title_active]
fg = "Reset"
modifiers = "BOLD | UNDERLINED"

[editor.block_insert]
fg = "LightGreen"
modifiers = "BOLD"

[editor.cursor_insert]
fg = "Green"
bg = "LightGreen"
modifiers = "RAPID_BLINK"

[msgbox]
error = "#105577"
question = "Magenta"
```
2 changes: 1 addition & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod sorter;
pub mod state;
#[cfg(test)]
mod test;
mod ui;
pub mod ui;

pub use runner::run;
pub use runner::HandleInputReturnType;
Expand Down
9 changes: 6 additions & 3 deletions src/app/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use backend::SqliteDataProvide;

use super::keymap::Input;
use super::ui::ui_functions::render_message_centered;
use super::ui::Styles;

#[derive(Debug, PartialEq, Eq)]
pub enum HandleInputReturnType {
Expand All @@ -27,6 +28,7 @@ pub enum HandleInputReturnType {
pub async fn run<B: Backend>(
terminal: &mut Terminal<B>,
settings: Settings,
styles: Styles,
pending_cmd: Option<PendingCliCommand>,
) -> Result<()> {
match settings.backend_type.unwrap_or_default() {
Expand All @@ -38,7 +40,7 @@ pub async fn run<B: Backend>(
crate::settings::json_backend::get_default_json_path()?
};
let data_provider = JsonDataProvide::new(path);
run_intern(terminal, data_provider, settings, pending_cmd).await
run_intern(terminal, data_provider, settings, styles, pending_cmd).await
}
#[cfg(not(feature = "json"))]
BackendType::Json => {
Expand All @@ -54,7 +56,7 @@ pub async fn run<B: Backend>(
crate::settings::sqlite_backend::get_default_sqlite_path()?
};
let data_provider = SqliteDataProvide::from_file(path).await?;
run_intern(terminal, data_provider, settings, pending_cmd).await
run_intern(terminal, data_provider, settings, styles, pending_cmd).await
}
#[cfg(not(feature = "sqlite"))]
BackendType::Sqlite => {
Expand All @@ -69,13 +71,14 @@ async fn run_intern<B, D>(
terminal: &mut Terminal<B>,
data_provider: D,
settings: Settings,
styles: Styles,
pending_cmd: Option<PendingCliCommand>,
) -> anyhow::Result<()>
where
B: Backend,
D: DataProvider,
{
let mut ui_components = UIComponents::new();
let mut ui_components = UIComponents::new(styles);
let mut app = App::new(data_provider, settings);
if let Some(cmd) = pending_cmd {
if let Err(err) = exec_pending_cmd(terminal, &app, cmd).await {
Expand Down
49 changes: 21 additions & 28 deletions src/app/ui/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifi
use ratatui::{
layout::Rect,
prelude::Margin,
style::{Color, Modifier, Style},
style::{Color, Style},
symbols,
widgets::{Block, Borders, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame,
Expand All @@ -15,9 +15,8 @@ use crate::app::{keymap::Input, runner::HandleInputReturnType, App};
use backend::DataProvider;
use tui_textarea::{CursorMove, Scrolling, TextArea};

use super::INACTIVE_CONTROL_COLOR;
use super::{commands::ClipboardOperation, EDITOR_MODE_COLOR};
use super::{ACTIVE_CONTROL_COLOR, VISUAL_MODE_COLOR};
use super::commands::ClipboardOperation;
use super::Styles;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorMode {
Expand Down Expand Up @@ -328,7 +327,7 @@ impl<'a> Editor<'a> {
self.mode = mode;
}

pub fn render_widget(&mut self, frame: &mut Frame, area: Rect) {
pub fn render_widget(&mut self, frame: &mut Frame, area: Rect, styles: &Styles) {
let mut title = "Content".to_owned();
if self.is_active {
let mode_caption = match self.mode {
Expand All @@ -342,17 +341,13 @@ impl<'a> Editor<'a> {
title.push_str(" *");
}

let estyles = &styles.editor;

let text_block_style = match (self.mode, self.is_active) {
(EditorMode::Insert, _) => Style::default()
.fg(EDITOR_MODE_COLOR)
.add_modifier(Modifier::BOLD),
(EditorMode::Visual, _) => Style::default()
.fg(VISUAL_MODE_COLOR)
.add_modifier(Modifier::BOLD),
(EditorMode::Normal, true) => Style::default()
.fg(ACTIVE_CONTROL_COLOR)
.add_modifier(Modifier::BOLD),
(EditorMode::Normal, false) => Style::default().fg(INACTIVE_CONTROL_COLOR),
(EditorMode::Insert, _) => estyles.block_insert,
(EditorMode::Visual, _) => estyles.block_visual,
(EditorMode::Normal, true) => estyles.block_normal_active,
(EditorMode::Normal, false) => estyles.block_normal_inactive,
};

self.text_area.set_block(
Expand All @@ -362,23 +357,21 @@ impl<'a> Editor<'a> {
.title(title),
);

let mut cursor_style = Style::default();
if self.is_active {
cursor_style = match self.mode {
EditorMode::Normal => cursor_style.bg(Color::White).fg(Color::Black),
EditorMode::Insert => cursor_style.bg(EDITOR_MODE_COLOR).fg(Color::Black),
EditorMode::Visual => cursor_style.bg(VISUAL_MODE_COLOR).fg(Color::Black),
let cursor_style = if self.is_active {
let s = match self.mode {
EditorMode::Normal => estyles.cursor_normal,
EditorMode::Insert => estyles.cursor_insert,
EditorMode::Visual => estyles.cursor_visual,
};
}
Style::from(s)
} else {
Style::reset()
};
self.text_area.set_cursor_style(cursor_style);

self.text_area.set_cursor_line_style(Style::default());
self.text_area.set_cursor_line_style(Style::reset());

self.text_area.set_style(
Style::default()
.fg(Color::Reset)
.remove_modifier(Modifier::BOLD),
);
self.text_area.set_style(Style::reset());

self.text_area
.set_selection_style(Style::default().bg(Color::White).fg(Color::Black));
Expand Down
Loading

0 comments on commit ceba728

Please sign in to comment.