feat(ui): comment out gpui ui code and improve iced ui logic
This commit is contained in:
@@ -1,262 +1,262 @@
|
||||
use ::tap::*;
|
||||
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use gpui::{
|
||||
App, Application, Bounds, ClickEvent, Context, ImageId, ImageSource, RenderImage, Resource,
|
||||
SharedString, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, rgb, size,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppState {
|
||||
pub title: SharedString,
|
||||
pub items: BTreeMap<SharedString, Item>,
|
||||
pub item_ids: BTreeMap<usize, SharedString>,
|
||||
pub current_item: Option<SharedString>,
|
||||
pub errors: Vec<String>,
|
||||
pub jellyfin_client: api::JellyfinClient,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Item {
|
||||
pub id: SharedString,
|
||||
pub name: SharedString,
|
||||
pub item_type: SharedString,
|
||||
pub media_type: SharedString,
|
||||
}
|
||||
|
||||
impl Render for AppState {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.size_full()
|
||||
.justify_center()
|
||||
.text_color(rgb(0xffffff))
|
||||
.child(Self::header())
|
||||
.child(Self::body(self, window, cx))
|
||||
.child(Self::footer())
|
||||
}
|
||||
}
|
||||
|
||||
actions!(jello_actions, [OpenItem, OnLoadItem, MouseDownEvent]);
|
||||
|
||||
impl AppState {
|
||||
fn new(title: impl AsRef<str>, jellyfin_client: api::JellyfinClient) -> Self {
|
||||
AppState {
|
||||
title: SharedString::new(title.as_ref()),
|
||||
items: BTreeMap::new(),
|
||||
item_ids: BTreeMap::new(),
|
||||
current_item: None,
|
||||
errors: Vec::new(),
|
||||
jellyfin_client,
|
||||
}
|
||||
}
|
||||
|
||||
// fn on_mouse_down(
|
||||
// &mut self,
|
||||
// event: &MouseDownEvent,
|
||||
// window: &mut Window,
|
||||
// cx: &mut Context<Self>,
|
||||
// ) {
|
||||
// // Handle mouse down event
|
||||
// }
|
||||
|
||||
fn load_item(id: usize) -> impl Fn(&mut Self, &ClickEvent, &mut Window, &mut Context<Self>) {
|
||||
move |state: &mut Self, event: &ClickEvent, window: &mut Window, cx: &mut Context<Self>| {
|
||||
let item_id = id;
|
||||
cx.spawn(async move |entity, app| {
|
||||
tracing::info!("Loading item with id: {}", item_id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn hover_item(id: usize) -> impl Fn(&mut Self, &bool, &mut Window, &mut Context<Self>) {
|
||||
move |state: &mut Self, item: &bool, window: &mut Window, cx: &mut Context<Self>| {
|
||||
dbg!("Hovering over item: {:?}", id);
|
||||
}
|
||||
}
|
||||
|
||||
fn header() -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.w_full()
|
||||
.justify_end()
|
||||
.h_20()
|
||||
.border_10()
|
||||
.bg(rgb(0x333333))
|
||||
.child(Self::button("Refresh"))
|
||||
}
|
||||
|
||||
fn footer() -> impl IntoElement {
|
||||
div().flex().flex_row().w_full().h_20().bg(rgb(0x333333))
|
||||
}
|
||||
|
||||
fn body(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.size_full()
|
||||
.child(Self::content(self, window, cx))
|
||||
.child(Self::sidebar(self, window, cx))
|
||||
}
|
||||
|
||||
fn button(label: &str) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.bg(rgb(0xff00ff))
|
||||
.text_color(rgb(0xffffff))
|
||||
.border_5()
|
||||
.rounded_lg()
|
||||
.child(label.to_string())
|
||||
}
|
||||
|
||||
fn content(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
div()
|
||||
.debug_below()
|
||||
.w_3_4()
|
||||
// .flex()
|
||||
// .flex_wrap()
|
||||
.bg(rgb(0x111111))
|
||||
.justify_start()
|
||||
.items_start()
|
||||
.overflow_hidden()
|
||||
.child(
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.justify_start()
|
||||
.items_start()
|
||||
.content_start()
|
||||
.gap_y_10()
|
||||
.gap_x_10()
|
||||
.border_t_10()
|
||||
.p_5()
|
||||
.child(Self::card(cx, 1))
|
||||
.child(Self::card(cx, 2))
|
||||
.child(Self::card(cx, 3))
|
||||
.child(Self::card(cx, 4))
|
||||
.child(Self::card(cx, 5))
|
||||
.child(Self::card(cx, 6))
|
||||
.child(Self::card(cx, 7))
|
||||
.child(Self::card(cx, 8))
|
||||
.child(Self::card(cx, 9)),
|
||||
)
|
||||
}
|
||||
|
||||
fn sidebar(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_1_4()
|
||||
.min_w_1_6()
|
||||
.bg(rgb(0x222222))
|
||||
.child(div().size_full().bg(gpui::yellow()))
|
||||
}
|
||||
|
||||
fn card(cx: &mut Context<AppState>, number: usize) -> impl IntoElement {
|
||||
div()
|
||||
.id(number)
|
||||
.on_click(cx.listener(Self::load_item(number)))
|
||||
.on_hover(cx.listener(Self::hover_item(number)))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_48()
|
||||
.h_64()
|
||||
.p_10()
|
||||
.bg(rgb(0xff00ff))
|
||||
.rounded_lg()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(jellyfin_client: api::JellyfinClient) {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|_, cx| cx.new(|_| AppState::new("Jello Media Browser", jellyfin_client)),
|
||||
)
|
||||
.expect("Failed to open window");
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Card {
|
||||
pub id: usize,
|
||||
pub title: SharedString,
|
||||
pub description: SharedString,
|
||||
pub image: SharedString,
|
||||
pub image_blurhash: BlurHash,
|
||||
pub media_type: SharedString,
|
||||
pub loading: bool,
|
||||
}
|
||||
|
||||
impl Render for Card {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_48()
|
||||
.h_64()
|
||||
.p_10()
|
||||
.bg(rgb(0xff00ff))
|
||||
.rounded_lg()
|
||||
.pipe(|card| {
|
||||
if self.loading {
|
||||
card.child(self.image_blurhash.clone())
|
||||
} else {
|
||||
card.child(gpui::img(self.image.clone()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlurHash {
|
||||
pub id: ImageId,
|
||||
pub data: Arc<RenderImage>,
|
||||
}
|
||||
|
||||
impl BlurHash {
|
||||
pub fn new(
|
||||
data: impl AsRef<str>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
punch: f32,
|
||||
) -> Result<Self, error_stack::Report<crate::Error>> {
|
||||
use error_stack::ResultExt;
|
||||
let decoded =
|
||||
blurhash::decode(data.as_ref(), width, height, punch).change_context(crate::Error)?;
|
||||
let buffer = image::RgbaImage::from_raw(width, height, decoded)
|
||||
.ok_or(crate::Error)
|
||||
.attach("Failed to convert")?;
|
||||
let frame = image::Frame::new(buffer);
|
||||
let render_image = RenderImage::new([frame]);
|
||||
Ok(Self {
|
||||
id: render_image.id,
|
||||
data: Arc::from(render_image),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for BlurHash {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
gpui::img(ImageSource::Render(self.data.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for BlurHash {
|
||||
type Element = gpui::Img;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
gpui::img(ImageSource::Render(self.data.clone()))
|
||||
}
|
||||
}
|
||||
// use ::tap::*;
|
||||
//
|
||||
// use std::{collections::BTreeMap, sync::Arc};
|
||||
//
|
||||
// use gpui::{
|
||||
// App, Application, Bounds, ClickEvent, Context, ImageId, ImageSource, RenderImage, Resource,
|
||||
// SharedString, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, rgb, size,
|
||||
// };
|
||||
//
|
||||
// #[derive(Clone, Debug)]
|
||||
// pub struct AppState {
|
||||
// pub title: SharedString,
|
||||
// pub items: BTreeMap<SharedString, Item>,
|
||||
// pub item_ids: BTreeMap<usize, SharedString>,
|
||||
// pub current_item: Option<SharedString>,
|
||||
// pub errors: Vec<String>,
|
||||
// pub jellyfin_client: api::JellyfinClient,
|
||||
// }
|
||||
//
|
||||
// #[derive(Clone, Debug)]
|
||||
// pub struct Item {
|
||||
// pub id: SharedString,
|
||||
// pub name: SharedString,
|
||||
// pub item_type: SharedString,
|
||||
// pub media_type: SharedString,
|
||||
// }
|
||||
//
|
||||
// impl Render for AppState {
|
||||
// fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
// div()
|
||||
// .flex()
|
||||
// .flex_col()
|
||||
// .size_full()
|
||||
// .justify_center()
|
||||
// .text_color(rgb(0xffffff))
|
||||
// .child(Self::header())
|
||||
// .child(Self::body(self, window, cx))
|
||||
// .child(Self::footer())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// actions!(jello_actions, [OpenItem, OnLoadItem, MouseDownEvent]);
|
||||
//
|
||||
// impl AppState {
|
||||
// fn new(title: impl AsRef<str>, jellyfin_client: api::JellyfinClient) -> Self {
|
||||
// AppState {
|
||||
// title: SharedString::new(title.as_ref()),
|
||||
// items: BTreeMap::new(),
|
||||
// item_ids: BTreeMap::new(),
|
||||
// current_item: None,
|
||||
// errors: Vec::new(),
|
||||
// jellyfin_client,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // fn on_mouse_down(
|
||||
// // &mut self,
|
||||
// // event: &MouseDownEvent,
|
||||
// // window: &mut Window,
|
||||
// // cx: &mut Context<Self>,
|
||||
// // ) {
|
||||
// // // Handle mouse down event
|
||||
// // }
|
||||
//
|
||||
// fn load_item(id: usize) -> impl Fn(&mut Self, &ClickEvent, &mut Window, &mut Context<Self>) {
|
||||
// move |state: &mut Self, event: &ClickEvent, window: &mut Window, cx: &mut Context<Self>| {
|
||||
// let item_id = id;
|
||||
// cx.spawn(async move |entity, app| {
|
||||
// tracing::info!("Loading item with id: {}", item_id);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn hover_item(id: usize) -> impl Fn(&mut Self, &bool, &mut Window, &mut Context<Self>) {
|
||||
// move |state: &mut Self, item: &bool, window: &mut Window, cx: &mut Context<Self>| {
|
||||
// dbg!("Hovering over item: {:?}", id);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn header() -> impl IntoElement {
|
||||
// div()
|
||||
// .flex()
|
||||
// .flex_row()
|
||||
// .w_full()
|
||||
// .justify_end()
|
||||
// .h_20()
|
||||
// .border_10()
|
||||
// .bg(rgb(0x333333))
|
||||
// .child(Self::button("Refresh"))
|
||||
// }
|
||||
//
|
||||
// fn footer() -> impl IntoElement {
|
||||
// div().flex().flex_row().w_full().h_20().bg(rgb(0x333333))
|
||||
// }
|
||||
//
|
||||
// fn body(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
// div()
|
||||
// .flex()
|
||||
// .flex_row()
|
||||
// .size_full()
|
||||
// .child(Self::content(self, window, cx))
|
||||
// .child(Self::sidebar(self, window, cx))
|
||||
// }
|
||||
//
|
||||
// fn button(label: &str) -> impl IntoElement {
|
||||
// div()
|
||||
// .flex()
|
||||
// .justify_center()
|
||||
// .items_center()
|
||||
// .bg(rgb(0xff00ff))
|
||||
// .text_color(rgb(0xffffff))
|
||||
// .border_5()
|
||||
// .rounded_lg()
|
||||
// .child(label.to_string())
|
||||
// }
|
||||
//
|
||||
// fn content(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
// div()
|
||||
// .debug_below()
|
||||
// .w_3_4()
|
||||
// // .flex()
|
||||
// // .flex_wrap()
|
||||
// .bg(rgb(0x111111))
|
||||
// .justify_start()
|
||||
// .items_start()
|
||||
// .overflow_hidden()
|
||||
// .child(
|
||||
// div()
|
||||
// .size_full()
|
||||
// .flex()
|
||||
// .flex_wrap()
|
||||
// .justify_start()
|
||||
// .items_start()
|
||||
// .content_start()
|
||||
// .gap_y_10()
|
||||
// .gap_x_10()
|
||||
// .border_t_10()
|
||||
// .p_5()
|
||||
// .child(Self::card(cx, 1))
|
||||
// .child(Self::card(cx, 2))
|
||||
// .child(Self::card(cx, 3))
|
||||
// .child(Self::card(cx, 4))
|
||||
// .child(Self::card(cx, 5))
|
||||
// .child(Self::card(cx, 6))
|
||||
// .child(Self::card(cx, 7))
|
||||
// .child(Self::card(cx, 8))
|
||||
// .child(Self::card(cx, 9)),
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// fn sidebar(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||
// div()
|
||||
// .flex()
|
||||
// .flex_col()
|
||||
// .w_1_4()
|
||||
// .min_w_1_6()
|
||||
// .bg(rgb(0x222222))
|
||||
// .child(div().size_full().bg(gpui::yellow()))
|
||||
// }
|
||||
//
|
||||
// fn card(cx: &mut Context<AppState>, number: usize) -> impl IntoElement {
|
||||
// div()
|
||||
// .id(number)
|
||||
// .on_click(cx.listener(Self::load_item(number)))
|
||||
// .on_hover(cx.listener(Self::hover_item(number)))
|
||||
// .flex()
|
||||
// .flex_col()
|
||||
// .w_48()
|
||||
// .h_64()
|
||||
// .p_10()
|
||||
// .bg(rgb(0xff00ff))
|
||||
// .rounded_lg()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn ui(jellyfin_client: api::JellyfinClient) {
|
||||
// Application::new().run(|cx: &mut App| {
|
||||
// let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
|
||||
// cx.open_window(
|
||||
// WindowOptions {
|
||||
// window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// |_, cx| cx.new(|_| AppState::new("Jello Media Browser", jellyfin_client)),
|
||||
// )
|
||||
// .expect("Failed to open window");
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// #[derive(Clone, Debug)]
|
||||
// pub struct Card {
|
||||
// pub id: usize,
|
||||
// pub title: SharedString,
|
||||
// pub description: SharedString,
|
||||
// pub image: SharedString,
|
||||
// pub image_blurhash: BlurHash,
|
||||
// pub media_type: SharedString,
|
||||
// pub loading: bool,
|
||||
// }
|
||||
//
|
||||
// impl Render for Card {
|
||||
// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
// div()
|
||||
// .id(self.id)
|
||||
// .flex()
|
||||
// .flex_col()
|
||||
// .w_48()
|
||||
// .h_64()
|
||||
// .p_10()
|
||||
// .bg(rgb(0xff00ff))
|
||||
// .rounded_lg()
|
||||
// .pipe(|card| {
|
||||
// if self.loading {
|
||||
// card.child(self.image_blurhash.clone())
|
||||
// } else {
|
||||
// card.child(gpui::img(self.image.clone()))
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[derive(Clone, Debug)]
|
||||
// pub struct BlurHash {
|
||||
// pub id: ImageId,
|
||||
// pub data: Arc<RenderImage>,
|
||||
// }
|
||||
//
|
||||
// impl BlurHash {
|
||||
// pub fn new(
|
||||
// data: impl AsRef<str>,
|
||||
// width: u32,
|
||||
// height: u32,
|
||||
// punch: f32,
|
||||
// ) -> Result<Self, error_stack::Report<crate::Error>> {
|
||||
// use error_stack::ResultExt;
|
||||
// let decoded =
|
||||
// blurhash::decode(data.as_ref(), width, height, punch).change_context(crate::Error)?;
|
||||
// let buffer = image::RgbaImage::from_raw(width, height, decoded)
|
||||
// .ok_or(crate::Error)
|
||||
// .attach("Failed to convert")?;
|
||||
// let frame = image::Frame::new(buffer);
|
||||
// let render_image = RenderImage::new([frame]);
|
||||
// Ok(Self {
|
||||
// id: render_image.id,
|
||||
// data: Arc::from(render_image),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Render for BlurHash {
|
||||
// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
// gpui::img(ImageSource::Render(self.data.clone()))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IntoElement for BlurHash {
|
||||
// type Element = gpui::Img;
|
||||
//
|
||||
// fn into_element(self) -> Self::Element {
|
||||
// gpui::img(ImageSource::Render(self.data.clone()))
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -190,7 +190,8 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
// if let Some(client) = state.jellyfin_client.clone() {
|
||||
match message {
|
||||
Message::Settings(msg) => settings::update(&mut state.settings, msg),
|
||||
Message::OpenItem(id) if let Some(client) = state.jellyfin_client.clone() => {
|
||||
Message::OpenItem(id) => {
|
||||
if let Some(client) = state.jellyfin_client.clone() {
|
||||
use api::jellyfin::BaseItemKind::*;
|
||||
if let Some(cached) = id.as_ref().and_then(|id| state.cache.get(id))
|
||||
&& matches!(cached._type, Video | Movie | Episode)
|
||||
@@ -214,6 +215,9 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Message::LoadedItem(id, items) => {
|
||||
state.cache.extend(id, items);
|
||||
@@ -221,9 +225,8 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
state.current = id;
|
||||
Task::none()
|
||||
}
|
||||
Message::Refresh if let Some(client) = state.jellyfin_client.clone() => {
|
||||
// Handle refresh logic
|
||||
// let client = state.jellyfin_client.clone();
|
||||
Message::Refresh => {
|
||||
if let Some(client) = state.jellyfin_client.clone() {
|
||||
let current = state.current;
|
||||
Task::perform(
|
||||
async move {
|
||||
@@ -238,6 +241,9 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
Ok(items) => Message::LoadedItem(msg, items),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Message::Error(err) => {
|
||||
tracing::error!("Error: {}", err);
|
||||
@@ -266,9 +272,10 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
// Handle search query change
|
||||
Task::none()
|
||||
}
|
||||
Message::Search if let Some(client) = state.jellyfin_client.clone() => {
|
||||
Message::Search => {
|
||||
// Handle search action
|
||||
// let client = state.jellyfin_client.clone();
|
||||
if let Some(client) = state.jellyfin_client.clone() {
|
||||
let query = state.query.clone().unwrap_or_default();
|
||||
Task::perform(async move { client.search(query).await }, |r| match r {
|
||||
Err(e) => Message::Error(format!("Search failed: {}", e)),
|
||||
@@ -277,6 +284,9 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
Message::LoadedItem(None, items)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Message::Video(msg) => video::update(state, msg),
|
||||
_ => todo!(),
|
||||
|
||||
Reference in New Issue
Block a user