styles: working on making it look just right

This commit is contained in:
Stevan Freeborn
2026-01-25 17:26:16 -06:00
parent bd9489feb4
commit 2c098b23b7
5 changed files with 204 additions and 109 deletions
+49 -19
View File
@@ -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}; use crate::message::{FileAction, Message, ViewAction};
pub fn view( pub fn view(
selected_file_action: Option<FileAction>, selected_file_action: Option<FileAction>,
selected_view_action: Option<ViewAction>, selected_view_action: Option<ViewAction>,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
let pick_list_padding = Padding {
let file_menu = pick_list( left: 4.0,
FileAction::ALL, right: 4.0,
selected_file_action, top: 2.0,
Message::FileActionSelected, bottom: 2.0,
) };
.placeholder("File");
let view_menu = pick_list( let file_menu = pick_list(
ViewAction::ALL, FileAction::ALL,
selected_view_action, selected_file_action,
Message::ViewActionSelected, Message::FileActionSelected,
) )
.placeholder("View"); .padding(pick_list_padding)
.style(pick_list_style)
.placeholder("File");
container( let view_menu = pick_list(
row![file_menu, view_menu].spacing(10) 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) .width(Length::Fill)
.into() .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
}
}
+17 -2
View File
@@ -1,5 +1,5 @@
use iced::{ use iced::{
Element, Length, Theme, highlighter, Element, Length, Theme, border, highlighter,
widget::{container, markdown, row, text_editor}, 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 { match mode {
Mode::Edit => { Mode::Edit => {
let editor = text_editor(file.content()) 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) .size(font_size)
.height(Length::Fill) .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); .on_action(Message::Edit);
container(row![editor]).height(Length::Fill).into() container(row![editor]).height(Length::Fill).into()
+19 -9
View File
@@ -1,16 +1,26 @@
use iced::widget::{container, row, text};
use iced::{Element, Length};
use crate::file::File; use crate::file::File;
use iced::widget::{container, row, text};
use iced::{Element, Length, Padding, Theme};
pub fn view(file: &File) -> Element<'_, crate::Message> { pub fn view(file: &File) -> Element<'_, crate::Message> {
let path_text = text(file.path_summary()); let path_text = text(file.path_summary()).size(12);
let cursor_text = text(file.position_summary()); let cursor_text = text(file.position_summary()).size(12);
container( container(row![cursor_text, path_text].spacing(20))
row![cursor_text, path_text] .style(|theme: &Theme| {
.spacing(20) let base = container::Style::default();
)
container::Style {
background: container::primary(theme).background,
..base
}
})
.width(Length::Fill) .width(Length::Fill)
.padding(5) .padding(Padding {
left: 20.0,
right: 20.0,
top: 5.0,
bottom: 5.0,
})
.into() .into()
} }
+62 -30
View File
@@ -1,41 +1,73 @@
use crate::file::File; use crate::file::File;
use crate::message::{FileAction, Message}; use crate::message::{FileAction, Message};
use iced::{Background, Border, Theme, border}; use iced::{Background, Border, Padding, Theme, border};
use iced::{ use iced::{
Element, Element,
widget::{button, container, row, scrollable, text}, widget::{button, container, row, scrollable, text},
}; };
pub fn view<'a>(files: &'a [File], active_index: usize) -> Element<'a, Message> { pub fn view<'a>(files: &'a [File], active_index: usize) -> Element<'a, Message> {
let tabs = files.iter().enumerate().map(|(index, file)| { let tabs = files.iter().enumerate().map(|(index, file)| {
let is_focused = index == active_index; let is_focused = index == active_index;
let label = text(file.display_name()); let label = text(file.display_name());
let close_btn = let close_btn =
button(text("x")).on_press(Message::FileActionSelected(FileAction::Close(Some(index)))); button(text("x")).padding(1).on_press(Message::FileActionSelected(FileAction::Close(Some(index))));
button(row![label, close_btn].spacing(5)) button(
.on_press(Message::SwitchTab(index)) row![label, close_btn]
.padding(5) .spacing(5)
.style(move |theme: &Theme, status| { .align_y(iced::Alignment::Center),
let base = button::primary(theme, status); )
let button_background = if is_focused { .on_press(Message::SwitchTab(index))
base.background .padding(Padding {
} else { top: 5.0,
Some(Background::Color(theme.palette().background)) bottom: 5.0,
}; left: 10.0,
right: 10.0,
})
.style(move |theme: &Theme, status: button::Status| {
let base = button::primary(theme, status);
button::Style { let button_background = if is_focused {
background: button_background, Some(Background::Color(theme.palette().primary))
border: Border { } else {
radius: border::radius(0).top_left(10).top_right(10), Some(Background::Color(theme.palette().background))
..base.border };
},
..base
}
})
.into()
});
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()
} }
+57 -49
View File
@@ -7,9 +7,10 @@ mod key_bindings;
mod message; mod message;
mod state; mod state;
use iced::Theme; use iced::theme::Palette;
use iced::widget::column; use iced::widget::column;
use iced::window::icon; use iced::window::icon;
use iced::{Color, Theme};
use iced::{Element, Subscription, Task, event}; use iced::{Element, Subscription, Task, event};
use iced::{keyboard, window}; use iced::{keyboard, window};
@@ -17,69 +18,76 @@ use crate::message::Message;
use crate::state::State; use crate::state::State;
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
iced::application(boot, update, view) iced::application(boot, update, view)
.subscription(subscription) .subscription(subscription)
.font(constants::CUSTOM_FONT_BYTES) .font(constants::CUSTOM_FONT_BYTES)
.theme(theme) .theme(theme)
.settings(iced::Settings { .settings(iced::Settings {
default_font: constants::CUSTOM_FONT, default_font: constants::CUSTOM_FONT,
..Default::default() ..Default::default()
}) })
.window(window::Settings { .window(window::Settings {
icon: Some( icon: Some(icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon")),
icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon"), ..window::Settings::default()
), })
..window::Settings::default() .run()
})
.run()
} }
fn boot() -> State { fn boot() -> State {
state::State::new() state::State::new()
} }
fn update(state: &mut State, message: Message) -> Task<Message> { fn update(state: &mut State, message: Message) -> Task<Message> {
match message { match message {
Message::Edit(action) => handler::edit(state, action), Message::Edit(action) => handler::edit(state, action),
Message::SwitchTab(index) => handler::switch_tab(state, index), Message::SwitchTab(index) => handler::switch_tab(state, index),
Message::LinkClicked(url) => handler::link_clicked(url), Message::LinkClicked(url) => handler::link_clicked(url),
Message::FileActionSelected(action) => handler::file_action(state, action), Message::FileActionSelected(action) => handler::file_action(state, action),
Message::ViewActionSelected(action) => handler::view_action(state, action), Message::ViewActionSelected(action) => handler::view_action(state, action),
Message::FileOpened(result) => handler::opened_file(state, result), Message::FileOpened(result) => handler::opened_file(state, result),
Message::FileSaved(result) => handler::saved_file(state, result), Message::FileSaved(result) => handler::saved_file(state, result),
} }
} }
fn view(state: &State) -> Element<'_, Message> { fn view(state: &State) -> Element<'_, Message> {
let current_file = state.active_file(); let current_file = state.active_file();
column![ column![
components::tabs::view(state.files(), state.current_file_index()), components::tabs::view(state.files(), state.current_file_index()),
components::action_bar::view(state.selected_file_action(), state.selected_view_action()), components::action_bar::view(state.selected_file_action(), state.selected_view_action()),
components::editor::view(current_file, state.mode(), state.font_size()), components::editor::view(current_file, state.mode(), state.font_size()),
components::status_bar::view(current_file), components::status_bar::view(current_file),
] ]
.padding(10) .into()
.into()
} }
fn subscription(_state: &State) -> Subscription<Message> { fn subscription(_state: &State) -> Subscription<Message> {
event::listen_with(|e, _status, _win| -> Option<Message> { event::listen_with(|e, _status, _win| -> Option<Message> {
match e { match e {
iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => {
for vb in key_bindings::ALL { for vb in key_bindings::ALL {
if vb.should_handle(&key, &modifiers) { if vb.should_handle(&key, &modifiers) {
return Some(vb.message()); return Some(vb.message());
} }
}
None
}
_ => None,
} }
})
None
}
_ => None,
}
})
} }
fn theme(_state: &State) -> Theme { 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),
},
)
} }