From 2c098b23b70115fa9717ba0f8aaaac1e18f2524d Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:26:16 -0600 Subject: [PATCH] styles: working on making it look just right --- src/components/action_bar.rs | 68 +++++++++++++++------- src/components/editor.rs | 19 ++++++- src/components/status_bar.rs | 28 ++++++--- src/components/tabs.rs | 92 ++++++++++++++++++++---------- src/main.rs | 106 +++++++++++++++++++---------------- 5 files changed, 204 insertions(+), 109 deletions(-) diff --git a/src/components/action_bar.rs b/src/components/action_bar.rs index 4dd0ac4..59162dd 100644 --- a/src/components/action_bar.rs +++ b/src/components/action_bar.rs @@ -1,29 +1,59 @@ -use iced::{Element, Length, widget::{container, pick_list, row}}; +use iced::{ + Background, Element, Length, Padding, Theme, + widget::{container, pick_list, row}, +}; use crate::message::{FileAction, Message, ViewAction}; pub fn view( - selected_file_action: Option, - selected_view_action: Option, + selected_file_action: Option, + selected_view_action: Option, ) -> Element<'static, Message> { - - let file_menu = pick_list( - FileAction::ALL, - selected_file_action, - Message::FileActionSelected, - ) - .placeholder("File"); + let pick_list_padding = Padding { + left: 4.0, + right: 4.0, + top: 2.0, + bottom: 2.0, + }; - let view_menu = pick_list( - ViewAction::ALL, - selected_view_action, - Message::ViewActionSelected, - ) - .placeholder("View"); + let file_menu = pick_list( + FileAction::ALL, + selected_file_action, + Message::FileActionSelected, + ) + .padding(pick_list_padding) + .style(pick_list_style) + .placeholder("File"); - container( - row![file_menu, view_menu].spacing(10) - ) + let view_menu = pick_list( + ViewAction::ALL, + selected_view_action, + Message::ViewActionSelected, + ) + .padding(pick_list_padding) + .style(pick_list_style) + .placeholder("View"); + + container(row![file_menu, view_menu].spacing(10)) + .style(|theme: &Theme| { + let base = container::Style::default(); + + container::Style { + background: container::primary(theme).background, + ..base + } + }) + .padding(5) .width(Length::Fill) .into() } + +fn pick_list_style(theme: &Theme, status: pick_list::Status) -> pick_list::Style { + let base = pick_list::default(theme, status); + + pick_list::Style { + placeholder_color: base.text_color, + background: Background::Color(theme.palette().primary), + ..base + } +} diff --git a/src/components/editor.rs b/src/components/editor.rs index 7133580..3fe24b9 100644 --- a/src/components/editor.rs +++ b/src/components/editor.rs @@ -1,5 +1,5 @@ use iced::{ - Element, Length, Theme, highlighter, + Element, Length, Theme, border, highlighter, widget::{container, markdown, row, text_editor}, }; @@ -9,9 +9,24 @@ pub fn view<'a>(file: &'a File, mode: Mode, font_size: u32) -> Element<'a, Messa match mode { Mode::Edit => { let editor = text_editor(file.content()) - .highlight(file.extension().unwrap_or("txt"), highlighter::Theme::Base16Ocean) + .highlight( + file.extension().unwrap_or("txt"), + highlighter::Theme::Base16Ocean, + ) + .padding(10) .size(font_size) .height(Length::Fill) + .style(|theme: &Theme, status: text_editor::Status| { + let base = text_editor::default(theme, status); + + text_editor::Style { + border: border::Border { + width: 0.0, + ..Default::default() + }, + ..base + } + }) .on_action(Message::Edit); container(row![editor]).height(Length::Fill).into() diff --git a/src/components/status_bar.rs b/src/components/status_bar.rs index a7cbeed..fe7317b 100644 --- a/src/components/status_bar.rs +++ b/src/components/status_bar.rs @@ -1,16 +1,26 @@ -use iced::widget::{container, row, text}; -use iced::{Element, Length}; use crate::file::File; +use iced::widget::{container, row, text}; +use iced::{Element, Length, Padding, Theme}; pub fn view(file: &File) -> Element<'_, crate::Message> { - let path_text = text(file.path_summary()); - let cursor_text = text(file.position_summary()); + let path_text = text(file.path_summary()).size(12); + let cursor_text = text(file.position_summary()).size(12); - container( - row![cursor_text, path_text] - .spacing(20) - ) + container(row![cursor_text, path_text].spacing(20)) + .style(|theme: &Theme| { + let base = container::Style::default(); + + container::Style { + background: container::primary(theme).background, + ..base + } + }) .width(Length::Fill) - .padding(5) + .padding(Padding { + left: 20.0, + right: 20.0, + top: 5.0, + bottom: 5.0, + }) .into() } diff --git a/src/components/tabs.rs b/src/components/tabs.rs index 842cf54..d679b03 100644 --- a/src/components/tabs.rs +++ b/src/components/tabs.rs @@ -1,41 +1,73 @@ use crate::file::File; use crate::message::{FileAction, Message}; -use iced::{Background, Border, Theme, border}; +use iced::{Background, Border, Padding, Theme, border}; use iced::{ - Element, - widget::{button, container, row, scrollable, text}, + Element, + widget::{button, container, row, scrollable, text}, }; pub fn view<'a>(files: &'a [File], active_index: usize) -> Element<'a, Message> { - let tabs = files.iter().enumerate().map(|(index, file)| { - let is_focused = index == active_index; + let tabs = files.iter().enumerate().map(|(index, file)| { + let is_focused = index == active_index; - let label = text(file.display_name()); - let close_btn = - button(text("x")).on_press(Message::FileActionSelected(FileAction::Close(Some(index)))); + let label = text(file.display_name()); + let close_btn = + button(text("x")).padding(1).on_press(Message::FileActionSelected(FileAction::Close(Some(index)))); - button(row![label, close_btn].spacing(5)) - .on_press(Message::SwitchTab(index)) - .padding(5) - .style(move |theme: &Theme, status| { - let base = button::primary(theme, status); - let button_background = if is_focused { - base.background - } else { - Some(Background::Color(theme.palette().background)) - }; + button( + row![label, close_btn] + .spacing(5) + .align_y(iced::Alignment::Center), + ) + .on_press(Message::SwitchTab(index)) + .padding(Padding { + top: 5.0, + bottom: 5.0, + left: 10.0, + right: 10.0, + }) + .style(move |theme: &Theme, status: button::Status| { + let base = button::primary(theme, status); - button::Style { - background: button_background, - border: Border { - radius: border::radius(0).top_left(10).top_right(10), - ..base.border - }, - ..base - } - }) - .into() - }); + let button_background = if is_focused { + Some(Background::Color(theme.palette().primary)) + } else { + Some(Background::Color(theme.palette().background)) + }; - scrollable(container(row(tabs).spacing(2))).into() + let button_style = button::Style { + background: button_background, + border: Border { + radius: border::radius(0).top_left(10).top_right(10), + ..base.border + }, + ..base + }; + + match status { + button::Status::Hovered => button::Style { + background: Some(Background::Color(theme.palette().primary)), + ..button_style + }, + _ => button_style, + } + }) + .into() + }); + + scrollable(container(row(tabs)).padding(Padding { + top: 5.0, + left: 5.0, + right: 5.0, + bottom: 0.0, + })) + .direction(scrollable::Direction::Horizontal( + scrollable::Scrollbar::new().spacing(1) + )) + .style(|theme: &Theme, status: scrollable::Status| { + let mut style = scrollable::default(theme, status); + style.horizontal_rail.background = Some(Background::Color(theme.palette().primary)); + style + }) + .into() } diff --git a/src/main.rs b/src/main.rs index 8e0b29b..e4515aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,10 @@ mod key_bindings; mod message; mod state; -use iced::Theme; +use iced::theme::Palette; use iced::widget::column; use iced::window::icon; +use iced::{Color, Theme}; use iced::{Element, Subscription, Task, event}; use iced::{keyboard, window}; @@ -17,69 +18,76 @@ use crate::message::Message; use crate::state::State; pub fn main() -> iced::Result { - iced::application(boot, update, view) - .subscription(subscription) - .font(constants::CUSTOM_FONT_BYTES) - .theme(theme) - .settings(iced::Settings { - default_font: constants::CUSTOM_FONT, - ..Default::default() - }) - .window(window::Settings { - icon: Some( - icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon"), - ), - ..window::Settings::default() - }) - .run() + iced::application(boot, update, view) + .subscription(subscription) + .font(constants::CUSTOM_FONT_BYTES) + .theme(theme) + .settings(iced::Settings { + default_font: constants::CUSTOM_FONT, + ..Default::default() + }) + .window(window::Settings { + icon: Some(icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon")), + ..window::Settings::default() + }) + .run() } fn boot() -> State { - state::State::new() + state::State::new() } fn update(state: &mut State, message: Message) -> Task { - match message { - Message::Edit(action) => handler::edit(state, action), - Message::SwitchTab(index) => handler::switch_tab(state, index), - Message::LinkClicked(url) => handler::link_clicked(url), - Message::FileActionSelected(action) => handler::file_action(state, action), - Message::ViewActionSelected(action) => handler::view_action(state, action), - Message::FileOpened(result) => handler::opened_file(state, result), - Message::FileSaved(result) => handler::saved_file(state, result), - } + match message { + Message::Edit(action) => handler::edit(state, action), + Message::SwitchTab(index) => handler::switch_tab(state, index), + Message::LinkClicked(url) => handler::link_clicked(url), + Message::FileActionSelected(action) => handler::file_action(state, action), + Message::ViewActionSelected(action) => handler::view_action(state, action), + Message::FileOpened(result) => handler::opened_file(state, result), + Message::FileSaved(result) => handler::saved_file(state, result), + } } fn view(state: &State) -> Element<'_, Message> { - let current_file = state.active_file(); + let current_file = state.active_file(); - column![ - components::tabs::view(state.files(), state.current_file_index()), - components::action_bar::view(state.selected_file_action(), state.selected_view_action()), - components::editor::view(current_file, state.mode(), state.font_size()), - components::status_bar::view(current_file), - ] - .padding(10) - .into() + column![ + components::tabs::view(state.files(), state.current_file_index()), + components::action_bar::view(state.selected_file_action(), state.selected_view_action()), + components::editor::view(current_file, state.mode(), state.font_size()), + components::status_bar::view(current_file), + ] + .into() } fn subscription(_state: &State) -> Subscription { - event::listen_with(|e, _status, _win| -> Option { - match e { - iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { - for vb in key_bindings::ALL { - if vb.should_handle(&key, &modifiers) { - return Some(vb.message()); - } - } - - None - } - _ => None, + event::listen_with(|e, _status, _win| -> Option { + match e { + iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { + for vb in key_bindings::ALL { + if vb.should_handle(&key, &modifiers) { + return Some(vb.message()); + } } - }) + + None + } + _ => None, + } + }) } fn theme(_state: &State) -> Theme { - Theme::Dark + Theme::custom( + "Win11 Dark", + Palette { + background: Color::from_rgb8(0x20, 0x20, 0x20), + text: Color::from_rgb8(0xF2, 0xF2, 0xF2), + primary: Color::from_rgb8(0x2C, 0x2C, 0x2C), + success: Color::from_rgb8(0x4C, 0xC3, 0x8A), + warning: Color::from_rgb8(0xF5, 0xC8, 0x4B), + danger: Color::from_rgb8(0xE8, 0x6A, 0x6A), + }, + ) }