Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⚡️ implement custom scrollable widget #173

Merged
merged 8 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions client/src/component/divider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub fn view() -> iced::Element<'static, crate::Message> {
iced::widget::column![iced::widget::horizontal_rule(1)]
.padding(iced::Padding::from([
1. * crate::REM,
0.,
0.5 * crate::REM,
0.,
]))
// We're fixing the height here to unify it
// with the height of entries for a smooth
// scrolling experience
.height(crate::ENTRY_HEIGHT)
.into()
}
25 changes: 16 additions & 9 deletions client/src/component/entry.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
pub fn view(entry: &crate::model::Entry, active: bool) -> iced::Element<'static, crate::Message> {
return iced::widget::container(
iced::widget::row![
iced::widget::text(clipped_title(entry.title.clone()))
.size(1. * crate::REM)
.width(iced::Length::Fill)
.shaping(iced::widget::text::Shaping::Advanced),
iced::widget::text(if active { &entry.action } else { "" }).size(1. * crate::REM)
]
.padding(0.5 * crate::REM),
iced::widget::container(
iced::widget::row![
iced::widget::text(clipped_title(entry.title.clone()))
.size(1. * crate::REM)
.width(iced::Length::Fill)
.shaping(iced::widget::text::Shaping::Advanced),
iced::widget::text(if active { &entry.action } else { "" }).size(1. * crate::REM)
]
.padding(0.5 * crate::REM),
)
.style(style(active)),
)
.style(style(active))
// We're fixing the height here to unify it
// with the height of plugin headers for a smooth
// scrolling experience
.height(crate::ENTRY_HEIGHT)
.padding(iced::Padding::from([0., 0.75 * crate::REM]))
.into();
}

Expand Down
3 changes: 2 additions & 1 deletion client/src/component/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod divider;
pub mod entry;
pub mod plugin;
pub mod plugin_header;
pub mod query_input;
39 changes: 0 additions & 39 deletions client/src/component/plugin.rs

This file was deleted.

21 changes: 21 additions & 0 deletions client/src/component/plugin_header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pub fn view(plugin: &crate::model::Plugin) -> iced::Element<'static, crate::Message> {
iced::widget::row![iced::widget::text(&plugin.title)
.font(iced::Font {
family: iced::font::Family::Name("FiraCode Nerd Font"),
weight: iced::font::Weight::Light,
stretch: iced::font::Stretch::Normal,
monospaced: true,
})
.size(0.75 * crate::REM)]
// We're fixing the height here to unify it
// with the height of entries for a smooth
// scrolling experience
.height(crate::ENTRY_HEIGHT)
.padding(iced::Padding::from([
0.8 * crate::REM,
1.25 * crate::REM,
0.5 * crate::REM,
1.25 * crate::REM,
]))
.into()
}
154 changes: 55 additions & 99 deletions client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ struct Centerpiece {
settings: settings::Settings,
}

pub const SCROLLABLE_ID: &str = "scrollable";
pub const APP_ID: &str = "centerpiece";

impl Application for Centerpiece {
Expand Down Expand Up @@ -253,24 +252,55 @@ impl Application for Centerpiece {
fn view(&self) -> iced::Element<Message> {
let entries = self.entries();

let mut lines =
iced::widget::column![].padding(iced::Padding::from([0., 0., 0.75 * crate::REM, 0.]));
let mut divider_added = true;
let mut header_added = false;
let mut next_entry_index_to_add = self.active_entry_index;

for lines_added in 0..11 {
if next_entry_index_to_add >= entries.len() {
break;
}

let mut plugin_to_add = None;
let mut last_plugin_start_index = 0;
for plugin in self.plugins.iter() {
if last_plugin_start_index == next_entry_index_to_add {
plugin_to_add = Some(plugin);
}
last_plugin_start_index += plugin.entries.len();
}

if !divider_added && plugin_to_add.is_some() {
lines = lines.push(component::divider::view());
divider_added = true;
continue;
}

if !header_added && plugin_to_add.is_some() {
lines = lines.push(component::plugin_header::view(plugin_to_add.unwrap()));
header_added = true;
continue;
} else if lines_added == 0 {
lines = lines.push(component::entry::view(
entries[next_entry_index_to_add - 1],
false,
));
}

lines = lines.push(component::entry::view(
entries[next_entry_index_to_add],
next_entry_index_to_add == self.active_entry_index,
));
divider_added = false;
header_added = false;
next_entry_index_to_add += 1;
}

iced::widget::container(iced::widget::column![
component::query_input::view(&self.query, !entries.is_empty()),
iced::widget::scrollable(iced::widget::column(
self.plugins
.iter()
.filter(|plugin| !plugin.entries.is_empty())
.enumerate()
.map(|(index, plugin)| component::plugin::view(
plugin,
index != 0,
self.active_entry_id()
))
.collect()
))
.id(iced::widget::scrollable::Id::new(SCROLLABLE_ID))
.style(iced::theme::Scrollable::Custom(Box::new(
ScrollableStyle {},
))),
lines
])
.style(iced::theme::Container::Custom(Box::new(
ApplicationWrapperStyle {},
Expand Down Expand Up @@ -302,7 +332,7 @@ impl Centerpiece {

let window = iced::window::Settings {
transparent: true,
size: (650, 400),
size: (650, 380),
decorations: false,
level: iced::window::Level::AlwaysOnTop,
resizable: false,
Expand Down Expand Up @@ -366,7 +396,7 @@ impl Centerpiece {

fn select_first_entry(&mut self) -> iced::Command<Message> {
self.active_entry_index = 0;
self.scroll_to_selected_entry()
iced::Command::none()
}

fn select_previous_entry(&mut self) -> iced::Command<Message> {
Expand All @@ -377,11 +407,11 @@ impl Centerpiece {

if self.active_entry_index == 0 {
self.active_entry_index = entries.len() - 1;
return self.scroll_to_selected_entry();
return iced::Command::none();
}

self.active_entry_index -= 1;
self.scroll_to_selected_entry()
iced::Command::none()
}

fn select_next_entry(&mut self) -> iced::Command<Message> {
Expand All @@ -391,41 +421,7 @@ impl Centerpiece {
}

self.active_entry_index += 1;
self.scroll_to_selected_entry()
}

fn scroll_to_selected_entry(&self) -> iced::Command<Message> {
let plugin_index = match self.active_entry_id() {
Some(active_entry_id) => self
.plugins
.iter()
.filter(|plugin| !plugin.entries.is_empty())
.position(|plugin| {
plugin
.entries
.iter()
.any(|entry| entry.id.eq(active_entry_id))
})
.unwrap_or(0) as f32,
None => 0.0,
};
let entry_index = self.active_entry_index as f32;

// 1.0 REM line height +
// 2x0.5 REM padding +
// 0.3 REM for good luck :D
let entry_height = 2.3 * crate::REM;
// 0.75 REM line height +
// 2x0.5 REM padding +
// 2x0.75 REM padding +
// 0.32 REM for good luck :D
let plugin_header_height = 3.57 * crate::REM;

let offset = (plugin_index * plugin_header_height) + (entry_index * entry_height);
iced::widget::scrollable::scroll_to(
iced::widget::scrollable::Id::new(SCROLLABLE_ID),
iced::widget::scrollable::AbsoluteOffset { x: 0.0, y: offset },
)
iced::Command::none()
}

fn select_next_plugin(&mut self) -> iced::Command<Message> {
Expand All @@ -442,7 +438,7 @@ impl Centerpiece {
.unwrap_or(self.active_entry_index);

self.active_entry_index = accumulated_entries;
self.scroll_to_selected_entry()
iced::Command::none()
}

fn select_previous_plugin(&mut self) -> iced::Command<Message> {
Expand All @@ -464,7 +460,7 @@ impl Centerpiece {
.unwrap_or(0);

self.active_entry_index = accumulated_entries;
self.scroll_to_selected_entry()
iced::Command::none()
}

fn register_plugin(&mut self, mut plugin: crate::model::Plugin) -> iced::Command<Message> {
Expand Down Expand Up @@ -523,6 +519,7 @@ impl Centerpiece {
}

pub const REM: f32 = 14.0;
pub const ENTRY_HEIGHT: f32 = 2.25 * crate::REM;

struct SandboxStyle {}
impl iced::application::StyleSheet for SandboxStyle {
Expand Down Expand Up @@ -555,44 +552,3 @@ impl iced::widget::container::StyleSheet for ApplicationWrapperStyle {
}
}
}

struct ScrollableStyle {}
impl iced::widget::scrollable::StyleSheet for ScrollableStyle {
type Style = iced::Theme;

fn active(&self, _style: &Self::Style) -> iced::widget::scrollable::Scrollbar {
let color_settings = crate::settings::Settings::get_or_init();
iced::widget::scrollable::Scrollbar {
background: None,
border_radius: iced::BorderRadius::from(0.),
border_width: 0.,
border_color: iced::Color::TRANSPARENT,
scroller: iced::widget::scrollable::Scroller {
color: settings::hexcolor(&color_settings.color.surface),
border_radius: iced::BorderRadius::from(0.25 * REM),
border_width: 4.,
border_color: settings::hexcolor(&color_settings.color.background),
},
}
}

fn hovered(
&self,
_style: &Self::Style,
_is_mouse_over_scrollbar: bool,
) -> iced::widget::scrollable::Scrollbar {
let color_settings = crate::settings::Settings::get_or_init();
iced::widget::scrollable::Scrollbar {
background: None,
border_radius: iced::BorderRadius::from(0.),
border_width: 0.,
border_color: iced::Color::TRANSPARENT,
scroller: iced::widget::scrollable::Scroller {
color: settings::hexcolor(&color_settings.color.surface),
border_radius: iced::BorderRadius::from(0.25 * REM),
border_width: 4.,
border_color: settings::hexcolor(&color_settings.color.background),
},
}
}
}