Commit b94697a5 authored by Felix Häcker's avatar Felix Häcker

Make StationFlowBox sortable

parent bec8ae65
......@@ -723,7 +723,7 @@ dependencies = [
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -827,7 +827,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.0.2"
version = "1.1.0"
source = "git+https://github.com/bluss/indexmap.git?rev=380e55c6a330b03f554f59ebd01c075bf396dd47#380e55c6a330b03f554f59ebd01c075bf396dd47"
[[package]]
name = "indexmap"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
......@@ -1701,6 +1706,7 @@ dependencies = [
"gstreamer 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-pbutils 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.1.0 (git+https://github.com/bluss/indexmap.git?rev=380e55c6a330b03f554f59ebd01c075bf396dd47)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libhandy 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -2271,7 +2277,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
"checksum indexmap 1.1.0 (git+https://github.com/bluss/indexmap.git?rev=380e55c6a330b03f554f59ebd01c075bf396dd47)" = "<none>"
"checksum indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4d6d89e0948bf10c08b9ecc8ac5b83f07f857ebe2c0cbe38de15b4e4f510356"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
......
......@@ -36,3 +36,4 @@ diesel = { version = "1.4.2", features = ["sqlite", "r2d2"] }
diesel_migrations = "1.4.0"
xdg = "2.2.0"
soup = { git="https://gitlab.gnome.org/haecker-felix/soup-rs", features = ["futures"] }
indexmap = { git = "https://github.com/bluss/indexmap.git", rev = "380e55c6a330b03f554f59ebd01c075bf396dd47" }
......@@ -12,8 +12,8 @@ use crate::audio::{PlaybackState, Player};
use crate::config;
use crate::database::Library;
use crate::discover::StoreFront;
use crate::model::{Order, Sorting};
use crate::ui::{View, Window};
use crate::utils::{Order, Sorting};
#[derive(Debug, Clone)]
pub enum Action {
......
......@@ -12,8 +12,8 @@ use crate::config;
use crate::database::connection;
use crate::database::queries;
use crate::database::StationIdentifier;
use crate::model::{Order, Sorting};
use crate::ui::StationFlowBox;
use crate::utils::{Order, Sorting};
pub struct Library {
pub widget: gtk::Box,
......@@ -35,33 +35,33 @@ impl Library {
welcome_text.set_text(format!("Welcome to {}", config::NAME).as_str());
let flowbox = Rc::new(StationFlowBox::new(sender.clone()));
flowbox.set_sorting(Sorting::Name, Order::Ascending);
content_box.add(&flowbox.widget);
let client = Client::new(Url::parse("http://www.radio-browser.info/webservice/").unwrap());
let library = Self { widget, flowbox, client, sender };
library.check_database();
library.update_flowbox();
library.load_stations();
library
}
pub fn add_stations(&self, stations: Vec<Station>) {
debug!("Add {} station(s)", stations.len());
self.flowbox.add_stations(stations.clone());
for station in stations {
let id = StationIdentifier::new(&station);
queries::insert_station_identifier(id).unwrap();
}
self.update_flowbox();
}
pub fn remove_stations(&self, stations: Vec<Station>) {
debug!("Remove {} station(s)", stations.len());
self.flowbox.remove_stations(stations.clone());
for station in stations {
let id = StationIdentifier::new(&station);
queries::delete_station_identifier(id).unwrap();
}
self.update_flowbox();
}
pub fn contains_station(station: &Station) -> bool {
......@@ -74,26 +74,24 @@ impl Library {
}
pub fn set_sorting(&self, sorting: Sorting, order: Order) {
//self.library_model.borrow_mut().set_sorting(sorting, order);
self.flowbox.set_sorting(sorting, order);
}
fn update_flowbox(&self) {
fn load_stations(&self) {
// Print database info
info!("Database Path: {}", connection::DB_PATH.to_str().unwrap());
info!("Stations: {}", queries::get_station_identifiers().unwrap().len());
// Load database async
let identifiers = queries::get_station_identifiers().unwrap();
let ctx = glib::MainContext::default();
let flowbox = self.flowbox.clone();
let fut = self.client.clone().get_stations_by_identifiers(identifiers).map(move |stations| {
flowbox.set_stations(stations);
flowbox.add_stations(stations);
});
let ctx = glib::MainContext::default();
ctx.spawn_local(fut);
}
fn check_database(&self) {
// Print database info
info!("Database Path: {}", connection::DB_PATH.to_str().unwrap());
info!("Stations: {}", queries::get_station_identifiers().unwrap().len());
}
}
quick_error! {
......
......@@ -69,7 +69,8 @@ impl Search {
let request = request.clone();
let fut = client.send_station_request(request).map(move |stations| {
debug!("{:?}", stations);
flowbox.set_stations(stations);
flowbox.clear();
flowbox.add_stations(stations);
});
let ctx = glib::MainContext::default();
......
#![feature(async_await, await_macro, async_closure)]
#[macro_use]
extern crate log;
extern crate pretty_env_logger;
......
......@@ -3,20 +3,3 @@ mod song_model;
pub use object_wrapper::ObjectWrapper;
pub use song_model::SongModel;
#[derive(Clone, Debug)]
pub enum Sorting {
Name,
Language,
Country,
State,
Codec,
Votes,
Bitrate,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Order {
Ascending,
Descending,
}
use glib::Sender;
use gtk::prelude::*;
use indexmap::IndexMap;
use std::cell::RefCell;
use std::convert::TryInto;
use crate::api::Station;
use crate::app::Action;
use crate::ui::station_row::StationRow;
use crate::utils;
use crate::utils::{Order, Sorting};
pub struct StationFlowBox {
pub widget: gtk::FlowBox,
stations: RefCell<IndexMap<i32, Station>>,
sorting: RefCell<Sorting>,
order: RefCell<Order>,
sender: Sender<Action>,
}
......@@ -16,6 +25,10 @@ impl StationFlowBox {
pub fn new(sender: Sender<Action>) -> Self {
let builder = gtk::Builder::new_from_resource("/de/haeckerfelix/Shortwave/gtk/station_flowbox.ui");
let widget: gtk::FlowBox = builder.get_object("station_flowbox").unwrap();
let stations = RefCell::new(IndexMap::new());
let sorting = RefCell::new(Sorting::Default);
let order = RefCell::new(Order::Ascending);
// Set automatically flowbox colums
let fb = widget.clone();
......@@ -32,26 +45,80 @@ impl StationFlowBox {
}
});
Self { widget, sender }
Self {
widget,
stations,
sorting,
order,
sender,
}
}
pub fn set_stations(&self, stations: Vec<Station>) {
self.clear();
pub fn add_stations(&self, stations: Vec<Station>) {
for station in stations {
if self.stations.borrow().contains_key(&station.id) {
warn!("Station \"{}\" is already added to flowbox.", station.name);
} else {
self.stations.borrow_mut().insert(station.id.clone(), station);
}
}
let widget = self.widget.downgrade();
let sender = self.sender.clone();
let constructor = move |station: Station| StationRow::new(sender.clone(), station).widget.clone();
self.sort();
self.update_rows();
}
// Start lazy loading
utils::lazy_load(stations.clone(), widget.clone(), constructor.clone());
pub fn remove_stations(&self, stations: Vec<Station>) {
for station in stations {
// Get the corresponding widget to the index, remove and destroy it
let index: usize = self.stations.borrow_mut().entry(station.id.clone()).index();
dbg!(index);
let widget = self.widget.get_child_at_index(index.try_into().unwrap()).unwrap();
self.widget.remove(&widget);
widget.destroy();
// Remove the station from the indexmap itself
self.stations.borrow_mut().shift_remove(&station.id);
}
}
pub fn set_sorting(&self, sorting: Sorting, order: Order) {
*self.sorting.borrow_mut() = sorting.clone();
*self.order.borrow_mut() = order.clone();
self.sort();
self.update_rows();
}
// Clears everything
fn clear(&self) {
pub fn clear(&self) {
self.stations.borrow_mut().clear();
self.clear_rows();
}
// Only destroy all rows, but don't clear the indexmap itself
fn clear_rows(&self) {
let children = self.widget.get_children();
for widget in children {
self.widget.remove(&widget);
widget.destroy();
}
}
fn update_rows(&self) {
self.clear_rows();
let widget = self.widget.downgrade();
let sender = self.sender.clone();
let stations = self.stations.borrow().clone();
let constructor = move |station: (i32, Station)| StationRow::new(sender.clone(), station.1).widget.clone();
// Start lazy loading
utils::lazy_load(stations.clone(), widget.clone(), constructor.clone());
}
fn sort(&self) {
self.stations
.borrow_mut()
.sort_by(move |_, a, _, b| utils::station_cmp(a, b, self.sorting.borrow().clone(), self.order.borrow().clone()));
}
}
use glib::{self, object::WeakRef};
use gtk::prelude::*;
use crate::api::Station;
#[derive(Clone, Debug)]
pub enum Sorting {
Default,
Name,
Language,
Country,
State,
Codec,
Votes,
Bitrate,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Order {
Ascending,
Descending,
}
pub fn station_cmp(a: &Station, b: &Station, sorting: Sorting, order: Order) -> std::cmp::Ordering {
let mut station_a = a.clone();
let mut station_b = b.clone();
if order == Order::Descending {
let tmp = station_a;
station_a = station_b;
station_b = tmp;
}
match sorting {
Sorting::Default => std::cmp::Ordering::Equal,
Sorting::Name => station_a.name.cmp(&station_b.name),
Sorting::Language => station_a.language.cmp(&station_b.language),
Sorting::Country => station_a.country.cmp(&station_b.country),
Sorting::State => station_a.state.cmp(&station_b.state),
Sorting::Codec => station_a.codec.cmp(&station_b.codec),
Sorting::Votes => station_a.votes.cmp(&station_b.votes),
Sorting::Bitrate => station_a.bitrate.parse::<i32>().unwrap().cmp(&station_b.bitrate.parse::<i32>().unwrap()),
}
}
// If you want to know more about lazy loading, you should read these:
// - https://en.wikipedia.org/wiki/Lazy_loading
// - https://blogs.gnome.org/ebassi/documentation/lazy-loading/comment-page-1/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment