Compare commits

...

15 Commits

Author SHA1 Message Date
de981eafe4 new file: README.md 2025-02-09 18:08:00 +03:00
45c6548c40 Merge branch 'appkit' 2025-02-08 12:40:40 +03:00
84fd4b9262 Integration start.
modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   src/main.rs
2025-02-08 12:31:08 +03:00
a792c79015 GUI with gtk4
modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   src/main.rs
	deleted:    src/view.rs
	deleted:    src/window.rs
2025-02-08 11:43:30 +03:00
7839160141 modified: src/main.rs
modified:   src/widget.rs
2024-12-22 18:29:10 +03:00
f42ef80bdd modified: src/theme.rs
modified:   src/widget.rs
2024-12-19 06:21:11 +03:00
b41e31b4be modified: src/main.rs
modified:   src/widget.rs
2024-12-15 18:44:50 +03:00
3d89e51418 modified: src/widget.rs 2024-12-14 05:21:43 +03:00
cb6beb71d8 GUI
modified:   src/main.rs
	modified:   src/theme.rs
	modified:   src/widget.rs
2024-12-14 05:12:03 +03:00
66722e8313 modified: src/theme.rs
modified:   src/widget.rs
2024-12-14 04:10:20 +03:00
1598329342 modified: src/main.rs
modified:   src/theme.rs
	modified:   src/widget.rs
2024-12-14 03:59:06 +03:00
2c671f6228 modified: src/theme.rs
modified:   src/widget.rs
2024-12-12 03:43:38 +03:00
d151a73b97 modified: src/main.rs
modified:   src/theme.rs
	modified:   src/widget.rs
2024-12-12 03:00:45 +03:00
ffd5eebfd2 modified: Cargo.lock
modified:   Cargo.toml
	modified:   src/main.rs
	new file:   src/theme.rs
	new file:   src/widget.rs
2024-12-12 02:54:49 +03:00
b89ab394ff Rewriting with Iced
modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   src/main.rs
	deleted:    src/view.rs
	deleted:    src/window.rs
2024-12-09 05:31:36 +02:00
8 changed files with 2391 additions and 240 deletions

2237
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -0,0 +1 @@
Abandoned. Take a look at [Lyrica](https://gitea.awain.net/alterwain/Lyrica)

View File

@ -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
View 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()
}
}

View File

@ -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
View 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 }
}
}

View File

@ -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);
}
}
}