Skip to content

Commit

Permalink
Merge pull request #177 from jhff/feat/command_bar
Browse files Browse the repository at this point in the history
Add a Command Bar
  • Loading branch information
tarkah authored Jul 31, 2023
2 parents 6b68ac0 + 4d7a8c4 commit f554a56
Show file tree
Hide file tree
Showing 14 changed files with 973 additions and 234 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ to ensure future breakages won't occur.**

Added:

- Command bar (opened by pressing (`Ctrl` + `K` (macOS: `CMD` + `K`)))
- Away-notify extension added for supported servers
- SASL support for PLAIN & EXTERNAL. The following per-server config keys have been added:
- PLAIN - `sasl.plain.username` & `sasl.plain.password`
Expand Down
21 changes: 21 additions & 0 deletions data/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,24 @@ pub enum Color {
#[default]
Unique,
}

#[derive(Debug, Clone, Copy)]
pub enum Resize {
None,
Maximize,
Restore,
}

impl Resize {
pub fn action(can_resize: bool, maximized: bool) -> Self {
if can_resize {
if maximized {
Self::Restore
} else {
Self::Maximize
}
} else {
Self::None
}
}
}
92 changes: 75 additions & 17 deletions data/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub use self::channel::Channel;
pub use self::dashboard::Dashboard;
pub use self::keys::Keys;
pub use self::server::Server;
use crate::environment;
use crate::palette::Palette;
use crate::server::Map as ServerMap;
use crate::theme::Palette;
use crate::{environment, Theme};

mod buffer;
pub mod channel;
Expand All @@ -21,11 +21,11 @@ mod keys;
pub mod server;

const CONFIG_TEMPLATE: &[u8] = include_bytes!("../../config.yaml");
const DEFAULT_THEME: (&str, &[u8]) = ("ferra", include_bytes!("../../assets/themes/ferra.yaml"));
const DEFAULT_THEME_FILE_NAME: &str = "ferra.yaml";

#[derive(Debug, Clone, Default)]
pub struct Config {
pub palette: Palette,
pub themes: Themes,
pub servers: ServerMap,
pub font: Font,
pub buffer: Buffer,
Expand All @@ -39,6 +39,21 @@ pub struct Font {
pub size: Option<u8>,
}

#[derive(Debug, Clone)]
pub struct Themes {
pub default: Theme,
pub all: Vec<Theme>,
}

impl Default for Themes {
fn default() -> Self {
Self {
default: Theme::default(),
all: vec![Theme::default()],
}
}
}

impl Config {
pub fn config_dir() -> PathBuf {
let dir = environment::config_dir();
Expand Down Expand Up @@ -95,11 +110,10 @@ impl Config {
} = serde_yaml::from_reader(BufReader::new(file))
.map_err(|e| Error::Parse(e.to_string()))?;

// If theme fails to load, use default Palette (Halloy theme)
let palette = Self::load_theme(&theme).unwrap_or_default();
let themes = Self::load_themes(&theme).unwrap_or_default();

Ok(Config {
palette,
themes,
servers,
font,
buffer,
Expand All @@ -108,21 +122,56 @@ impl Config {
})
}

fn load_theme(theme: &str) -> Result<Palette, Error> {
fn load_themes(default_key: &str) -> Result<Themes, Error> {
#[derive(Deserialize)]
pub struct Theme {
pub struct Data {
#[serde(default)]
pub name: String,
#[serde(default)]
pub palette: Palette,
}

let path = Self::themes_dir().join(format!("{theme}.yaml"));
let file = File::open(path).map_err(|e| Error::Read(e.to_string()))?;
let Theme { palette, .. } = serde_yaml::from_reader(BufReader::new(file))
.map_err(|e| Error::Parse(e.to_string()))?;
let read_entry = |entry: fs::DirEntry| {
let content = fs::read(entry.path())?;

let Data { name, palette } =
serde_yaml::from_slice(&content).map_err(|e| Error::Parse(e.to_string()))?;

Ok::<Theme, Error>(Theme::new(name, &palette))
};

let mut all = vec![];
let mut default = Theme::default();
let mut has_halloy_theme = false;

for entry in fs::read_dir(Self::themes_dir())? {
let Ok(entry) = entry else {
continue;
};

Ok(palette)
let Some(file_name) = entry.file_name().to_str().map(String::from) else {
continue;
};

if file_name.ends_with(".yaml") {
if let Ok(theme) = read_entry(entry) {
if file_name.strip_suffix(".yaml").unwrap_or_default() == default_key {
default = theme.clone();
}
if file_name == DEFAULT_THEME_FILE_NAME {
has_halloy_theme = true;
}

all.push(theme);
}
}
}

if !has_halloy_theme {
all.push(Theme::default());
}

Ok(Themes { default, all })
}

pub fn create_template_config() {
Expand All @@ -139,11 +188,12 @@ impl Config {
}

pub fn create_themes_dir() {
const CONTENT: &[u8] = include_bytes!("../../assets/themes/ferra.yaml");

// Create default theme file.
let (theme, content) = DEFAULT_THEME;
let file = Config::themes_dir().join(format!("{theme}.yaml"));
let file = Config::themes_dir().join(DEFAULT_THEME_FILE_NAME);
if !file.exists() {
let _ = fs::write(file, content);
let _ = fs::write(file, CONTENT);
}
}

Expand All @@ -152,5 +202,13 @@ pub enum Error {
#[error("config could not be read: {0}")]
Read(String),
#[error("{0}")]
Io(String),
#[error("{0}")]
Parse(String),
}

impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Self::Io(error.to_string())
}
}
4 changes: 2 additions & 2 deletions data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ pub use self::dashboard::Dashboard;
pub use self::input::Input;
pub use self::message::Message;
pub use self::mode::Mode;
pub use self::palette::Palette;
pub use self::pane::Pane;
pub use self::server::Server;
pub use self::shortcut::Shortcut;
pub use self::theme::Theme;
pub use self::user::User;

pub mod buffer;
Expand All @@ -26,10 +26,10 @@ pub mod input;
pub mod log;
pub mod message;
pub mod mode;
pub mod palette;
pub mod pane;
pub mod server;
pub mod shortcut;
pub mod stream;
pub mod theme;
pub mod time;
pub mod user;
102 changes: 102 additions & 0 deletions data/src/palette.rs → data/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,108 @@ use palette::{DarkenAssign, FromColor, LightenAssign, Mix, Okhsl, Srgb};
use rand::prelude::*;
use rand_chacha::ChaChaRng;

const DEFAULT_THEME_NAME: &str = "Ferra";

#[derive(Debug, Clone)]
pub struct Theme {
pub name: String,
pub colors: Colors,
}

impl Theme {
pub fn new(name: String, palette: &Palette) -> Self {
Theme {
name,
colors: Colors::new(palette),
}
}
}

impl Default for Theme {
fn default() -> Self {
Self {
name: DEFAULT_THEME_NAME.to_string(),
colors: Colors::new(&Palette::default()),
}
}
}

#[derive(Debug, Clone)]
pub struct Colors {
pub background: Subpalette,
pub text: Subpalette,
pub action: Subpalette,
pub accent: Subpalette,
pub alert: Subpalette,
pub error: Subpalette,
pub info: Subpalette,
pub success: Subpalette,
}

impl Colors {
pub fn new(palette: &Palette) -> Self {
Colors {
background: Subpalette::from_color(palette.background, palette),
text: Subpalette::from_color(palette.text, palette),
action: Subpalette::from_color(palette.action, palette),
accent: Subpalette::from_color(palette.accent, palette),
alert: Subpalette::from_color(palette.alert, palette),
error: Subpalette::from_color(palette.error, palette),
info: Subpalette::from_color(palette.info, palette),
success: Subpalette::from_color(palette.success, palette),
}
}
}

#[derive(Debug, Clone)]
pub struct Subpalette {
pub base: Color,
pub light: Color,
pub lighter: Color,
pub lightest: Color,
pub dark: Color,
pub darker: Color,
pub darkest: Color,
pub low_alpha: Color,
pub med_alpha: Color,
pub high_alpha: Color,
}

impl Subpalette {
pub fn from_color(color: Color, palette: &Palette) -> Subpalette {
let is_dark = is_dark(palette.background);

Subpalette {
base: color,
light: lighten(color, 0.03),
lighter: lighten(color, 0.06),
lightest: lighten(color, 0.12),
dark: darken(color, 0.03),
darker: darken(color, 0.06),
darkest: darken(color, 0.12),
low_alpha: if is_dark {
alpha(color, 0.4)
} else {
alpha(color, 0.8)
},
med_alpha: if is_dark {
alpha(color, 0.2)
} else {
alpha(color, 0.4)
},
high_alpha: if is_dark {
alpha(color, 0.1)
} else {
alpha(color, 0.3)
},
}
}

pub fn is_dark(&self) -> bool {
is_dark(self.base)
}
}

#[derive(Debug, Clone, Copy)]
pub struct Palette {
pub background: Color,
Expand Down
5 changes: 5 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum Event {
Escape,
Home,
End,
CommandBar,
}

pub fn events() -> Subscription<Event> {
Expand All @@ -33,6 +34,10 @@ fn filtered_events(event: iced::Event, status: iced::event::Status) -> Option<Ev
key_code: keyboard::KeyCode::End,
..
}) if ignored(status) => Some(Event::End),
iced::Event::Keyboard(keyboard::Event::KeyPressed {
key_code: keyboard::KeyCode::K,
modifiers,
}) if modifiers.command() => Some(Event::CommandBar),
iced::Event::Window(window::Event::CloseRequested) => Some(Event::CloseRequested),
_ => None,
}
Expand Down
17 changes: 12 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl Halloy {
(
Halloy {
screen,
theme: Theme::new_from_palette(config.palette),
theme: config.themes.default.clone().into(),
clients: Default::default(),
servers: config.servers.clone(),
config,
Expand Down Expand Up @@ -168,7 +168,7 @@ pub enum Message {
impl Application for Halloy {
type Executor = executor::Default;
type Message = Message;
type Theme = theme::Theme;
type Theme = Theme;
type Flags = Result<Config, config::Error>;

fn new(config_load: Self::Flags) -> (Halloy, Command<Self::Message>) {
Expand All @@ -191,8 +191,13 @@ impl Application for Halloy {
return Command::none();
};

let command =
dashboard.update(message, &mut self.clients, &mut self.servers, &self.config);
let command = dashboard.update(
message,
&mut self.clients,
&mut self.servers,
&mut self.theme,
&self.config,
);
// Retrack after dashboard state changes
let track = dashboard.track();

Expand Down Expand Up @@ -352,7 +357,9 @@ impl Application for Halloy {
}
Message::Event(event) => {
if let Screen::Dashboard(dashboard) = &mut self.screen {
dashboard.handle_event(event).map(Message::Dashboard)
dashboard
.handle_event(event, &self.clients, &self.config, &mut self.theme)
.map(Message::Dashboard)
} else if let event::Event::CloseRequested = event {
window::close()
} else {
Expand Down
Loading

0 comments on commit f554a56

Please sign in to comment.