Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
de981eafe4 | |||
45c6548c40 | |||
84fd4b9262 | |||
a792c79015 | |||
7839160141 | |||
f42ef80bdd | |||
b41e31b4be | |||
3d89e51418 | |||
cb6beb71d8 | |||
66722e8313 | |||
1598329342 | |||
2c671f6228 | |||
d151a73b97 | |||
ffd5eebfd2 | |||
b89ab394ff |
2237
Cargo.lock
generated
2237
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,4 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
rusb = "0.9.4"
|
||||
regex = "1.11.1"
|
||||
cacao = "0.3.2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
async-channel = "2.3.1"
|
||||
gtk = { version = "0.9.5", package = "gtk4", features = ["v4_16"] }
|
||||
soundcloud = { git = "https://gitea.awain.net/alterwain/soundcloud_api.git" }
|
1
README.md
Normal file
1
README.md
Normal file
@ -0,0 +1 @@
|
||||
Abandoned. Take a look at [Lyrica](https://gitea.awain.net/alterwain/Lyrica)
|
114
src/main.rs
114
src/main.rs
@ -1,41 +1,21 @@
|
||||
use cacao::appkit::window::{TitleVisibility, Window, WindowConfig, WindowController};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::button::Button;
|
||||
use cacao::view::{SplitViewController, View, ViewDelegate};
|
||||
use gtk::{prelude::*, ApplicationWindow, Orientation, Stack, StackSidebar};
|
||||
use gtk::{glib, Application};
|
||||
use glib::clone;
|
||||
|
||||
use crate::view::details_view::Details;
|
||||
use crate::view::sidebar::MainSidebar;
|
||||
use crate::view::content_view::ScreenView;
|
||||
use crate::window::main_window::MainWindow;
|
||||
use std::{sync::OnceLock, error::Error};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
|
||||
mod disk_util;
|
||||
mod ipod_util;
|
||||
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
const VENDOR_ID: u16 = 1452;
|
||||
const PRODUCT_ID: u16 = 4617;
|
||||
|
||||
struct ILoaderApp {
|
||||
window: WindowController<MainWindow>
|
||||
}
|
||||
const APP_ID: &str = "com.alterdekim.iloader";
|
||||
|
||||
impl AppDelegate for ILoaderApp {
|
||||
/// There should be stuff which loads underlying logic for communication with IPod
|
||||
fn will_finish_launching(&self) {}
|
||||
|
||||
fn did_finish_launching(&self) {
|
||||
App::activate();
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
fn should_terminate_after_last_window_closed(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> glib::ExitCode {
|
||||
/*for device in rusb::devices().unwrap().iter() {
|
||||
let device_desc = device.device_descriptor().unwrap();
|
||||
if VENDOR_ID == device_desc.vendor_id() && PRODUCT_ID == device_desc.product_id() {
|
||||
@ -43,9 +23,77 @@ fn main() {
|
||||
println!("{}", ipod_util::get_ipod_path().is_some());
|
||||
}
|
||||
}*/
|
||||
let config = WindowConfig::default();
|
||||
// Create a new application
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
|
||||
App::new("com.alterdekim.iloader", ILoaderApp {
|
||||
window: WindowController::with(config, MainWindow::default())
|
||||
}).run();
|
||||
app.connect_activate(build_ui);
|
||||
|
||||
// Run the application
|
||||
app.run()
|
||||
}
|
||||
|
||||
fn runtime() -> &'static Runtime {
|
||||
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
|
||||
RUNTIME.get_or_init(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."))
|
||||
}
|
||||
|
||||
fn build_ui(app: &Application) {
|
||||
let (sender, receiver) = async_channel::bounded::<CloudPlaylists>(1);
|
||||
runtime().spawn(clone!(
|
||||
#[strong]
|
||||
sender,
|
||||
async move {
|
||||
let app_version = soundcloud::get_app().await.unwrap().unwrap();
|
||||
let client_id = soundcloud::get_client_id().await.unwrap().unwrap();
|
||||
let user_id: u64 = 774639751;
|
||||
|
||||
sender
|
||||
.send(soundcloud::get_playlists(user_id, client_id, app_version).await.unwrap())
|
||||
.await
|
||||
.expect("The channel needs to be open.");
|
||||
}
|
||||
));
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("ILoader")
|
||||
.default_width(980)
|
||||
.default_height(700)
|
||||
.build();
|
||||
|
||||
let hbox = gtk::Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
let stack = Stack::new();
|
||||
stack.set_transition_type(gtk::StackTransitionType::SlideLeftRight); // Add some pages to the stack
|
||||
|
||||
let label1 = gtk::Label::new(Some("Youtube Content"));
|
||||
stack.add_titled(&label1, Some("page1"), "Youtube");
|
||||
|
||||
let grid_layout = gtk::Grid::builder()
|
||||
.row_spacing(5)
|
||||
.column_spacing(5)
|
||||
.build();
|
||||
|
||||
window.set_child(Some(&hbox));
|
||||
|
||||
let label3 = gtk::Label::new(Some("Spotify Content"));
|
||||
stack.add_titled(&label3, Some("page3"), "Spotify");
|
||||
|
||||
let sidebar = StackSidebar::new();
|
||||
sidebar.set_stack(&stack);
|
||||
|
||||
hbox.append(&sidebar);
|
||||
hbox.append(&stack);
|
||||
|
||||
glib::spawn_future_local(async move {
|
||||
while let Ok(response) = receiver.recv().await {
|
||||
for playlist in response.collection {
|
||||
let label2 = gtk::Label::new(Some(&playlist.title));
|
||||
grid_layout.attach(&label2, 0, 0, 1, 1);
|
||||
}
|
||||
stack.add_titled(&grid_layout, Some("page2"), "Soundcloud");
|
||||
}
|
||||
});
|
||||
|
||||
window.present();
|
||||
}
|
41
src/theme.rs
Normal file
41
src/theme.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use iced::theme::Palette;
|
||||
use iced::widget::button::{Status, Style};
|
||||
use iced::{Background, Border, Color, Font, Shadow, Vector};
|
||||
use iced::Length::Fill;
|
||||
use iced::{widget::container, window, Theme, Element, Settings, Task as Command};
|
||||
use iced::widget::{button, column, pick_list, radio, text, Column, Container, scrollable};
|
||||
|
||||
pub const SF_FONT: iced::Font = Font {
|
||||
family: iced::font::Family::Name("SF Pro Text"),
|
||||
weight: iced::font::Weight::Normal,
|
||||
stretch: iced::font::Stretch::Normal,
|
||||
style: iced::font::Style::Normal,
|
||||
};
|
||||
|
||||
pub fn get_default_theme() -> Theme {
|
||||
Theme::custom("Glossy".to_string(), Palette {
|
||||
background: Color::from_rgba8(255, 255, 255, 1.0), // Color::from_rgba8(244, 245, 245, 1.0)
|
||||
text: Color::from_rgb8(0, 0, 0),
|
||||
primary: Color::from_rgb8(0, 122, 255),
|
||||
success: Color::from_rgb8(52, 199, 89),
|
||||
danger: Color::from_rgb8(255, 59, 48),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn basic_button_theme(theme: &Theme, _status: Status) -> Style {
|
||||
Style {
|
||||
background: Some(Background::Color(Color::from_rgb8(255, 255, 255))),
|
||||
text_color: Color::from_rgb8(0, 0, 0), // theme.palette().text
|
||||
border: Border {
|
||||
color: Color::TRANSPARENT,
|
||||
width: 0.0,
|
||||
radius: 5.0.into(),
|
||||
},
|
||||
shadow: Shadow {
|
||||
color: Color::from_rgba8(0, 0, 0, 0.1),
|
||||
offset: Vector::new(0.0, 1.0),
|
||||
blur_radius: 0.1,
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
60
src/view.rs
60
src/view.rs
@ -1,60 +0,0 @@
|
||||
|
||||
pub mod sidebar {
|
||||
use cacao::{appkit::FocusRingType, button::{BezelStyle, Button}, geometry::Rect, layout::Layout, text::Font, view::{View, ViewDelegate}};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MainSidebar {
|
||||
|
||||
}
|
||||
|
||||
impl ViewDelegate for MainSidebar {
|
||||
const NAME: &'static str = "MainSidebar";
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
let mut btn = Button::new("testtesttest");
|
||||
btn.set_bezel_style(BezelStyle::TexturedRounded);
|
||||
btn.set_bordered(false);
|
||||
btn.set_font(Font::system(14.));
|
||||
btn.set_action(|| {
|
||||
println!("HEY");
|
||||
});
|
||||
view.add_subview(&btn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod content_view {
|
||||
use cacao::{button::Button, layout::Layout, view::{View, ViewDelegate}};
|
||||
|
||||
pub struct ScreenView {
|
||||
pub btn: Button
|
||||
}
|
||||
|
||||
impl Default for ScreenView {
|
||||
fn default() -> Self {
|
||||
let btn = Button::new("test");
|
||||
Self { btn }
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewDelegate for ScreenView {
|
||||
const NAME: &'static str = "ScreenView";
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
view.add_subview(&self.btn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod details_view {
|
||||
use cacao::{button::Button, layout::Layout, view::{View, ViewDelegate}};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Details {
|
||||
|
||||
}
|
||||
|
||||
impl ViewDelegate for Details {
|
||||
const NAME: &'static str = "Details";
|
||||
}
|
||||
}
|
142
src/widget.rs
Normal file
142
src/widget.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use iced::{widget::{button, column, container, row, text, Column}, Element, Length::Fill, Padding, Task as Command};
|
||||
|
||||
use crate::{theme, Message};
|
||||
|
||||
|
||||
pub fn basic_btn(s: String) -> button::Button<'static, Message> {
|
||||
button(container(text(s).center().font(theme::SF_FONT).size(13.0).line_height(1.)).padding(Padding {
|
||||
top: 1.5,
|
||||
right: 2.,
|
||||
bottom: 1.5,
|
||||
left: 2.,
|
||||
})).style(theme::basic_button_theme)
|
||||
}
|
||||
|
||||
// the value T should be something, that inherits ActionWindow
|
||||
#[derive(Clone)]
|
||||
pub enum SidebarTab {
|
||||
Youtube(String),
|
||||
Spotify(String),
|
||||
Soundcloud(String),
|
||||
ITunes(String),
|
||||
Playlists(String),
|
||||
FileSystem(String),
|
||||
Metadata(String),
|
||||
FindCopies(String),
|
||||
Settings(String)
|
||||
}
|
||||
|
||||
impl Into<String> for SidebarTab {
|
||||
fn into(self) -> String {
|
||||
match self {
|
||||
SidebarTab::Youtube(a) => a,
|
||||
SidebarTab::Spotify(a) => a,
|
||||
SidebarTab::Soundcloud(a) => a,
|
||||
SidebarTab::ITunes(a) => a,
|
||||
SidebarTab::Playlists(a) => a,
|
||||
SidebarTab::FileSystem(a) => a,
|
||||
SidebarTab::Metadata(a) => a,
|
||||
SidebarTab::FindCopies(a) => a,
|
||||
SidebarTab::Settings(a) => a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SidebarGroup {
|
||||
tabs: Vec<(SidebarTab, Box<dyn ActionWindow>)>,
|
||||
name: String
|
||||
}
|
||||
|
||||
impl SidebarGroup {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self{tabs: Vec::new(), name}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn push_tab(&mut self, t: (SidebarTab, Box<dyn ActionWindow>)) {
|
||||
self.tabs.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SidebarGroup> for Element<'_, Message> {
|
||||
fn from(value: &SidebarGroup) -> Self {
|
||||
container(column![
|
||||
text(value.name.clone()),
|
||||
column(
|
||||
value.tabs
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let s: String = i.0.clone().into();
|
||||
basic_btn(s).into()
|
||||
})
|
||||
.collect::<Vec<Element<Message>>>()
|
||||
)
|
||||
]).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActionWindow {
|
||||
fn view(&self) -> Element<Message>;
|
||||
fn update(&mut self, message: Message) -> Command<Message>;
|
||||
}
|
||||
|
||||
pub struct SettingsWindow {}
|
||||
|
||||
impl ActionWindow for SettingsWindow {
|
||||
fn view(&self) -> Element<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct YTWindow {}
|
||||
|
||||
impl ActionWindow for YTWindow {
|
||||
fn view(&self) -> Element<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SplitView {
|
||||
sidebar: Vec<SidebarGroup>,
|
||||
selected: Option<(SidebarTab, Box<dyn ActionWindow>)>
|
||||
}
|
||||
|
||||
impl SplitView {
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
row![
|
||||
column(
|
||||
self.sidebar.iter()
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<Element<Message>>>()
|
||||
),
|
||||
if self.selected.is_some() { container((&self.selected.as_ref().unwrap().1).view()) } else { container(text!("")) }
|
||||
].into()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Command<Message> {
|
||||
if self.selected.is_some() {
|
||||
let a = &mut self.selected.as_mut().unwrap().1;
|
||||
return a.update(message);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
|
||||
pub fn push_group(&mut self, group: SidebarGroup) {
|
||||
self.sidebar.push(group);
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self{ sidebar: Vec::new(), selected: None }
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
pub mod main_window {
|
||||
use cacao::{appkit::window::{TitleVisibility, Window, WindowDelegate}, view::SplitViewController};
|
||||
|
||||
use crate::view::{content_view::ScreenView, details_view::Details, sidebar::MainSidebar};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MainWindow {
|
||||
split_view_controller: Option<SplitViewController<MainSidebar, ScreenView, Details>>
|
||||
}
|
||||
|
||||
impl WindowDelegate for MainWindow {
|
||||
const NAME: &'static str = "MainWindow";
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
window.set_title("ILoader");
|
||||
window.set_title_visibility(TitleVisibility::Hidden);
|
||||
window.set_titlebar_appears_transparent(true);
|
||||
window.set_movable_by_background(true);
|
||||
window.set_autosave_name("CacaoILoader");
|
||||
window.set_minimum_content_size(980., 700.);
|
||||
|
||||
let split_view_controller = SplitViewController::new(MainSidebar::default(),
|
||||
ScreenView::default(),
|
||||
Some(Details::default())); // Some(DetailView::default())
|
||||
|
||||
window.set_content_view_controller(&split_view_controller);
|
||||
|
||||
self.split_view_controller = Some(split_view_controller);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user