Repository: aome510/spotify-player (language: rust)

Issue:
Implementation of adjustable widget sizes
While working on implementing different sized widget in the library, I have come across two different way to implement the setting of different widget sizes in the config. 

### Option 1

Have two config values `album width` & `playlist width` and have them be the percentage values used in the layout. 

#### Pros 
- Super simple to understand

#### Cons 
- Having two values to configure the size of the widgets seems very unclean 
- It is easily possible to write values which are too large and would then cause errors, which need to be checked for 

### Option 2

Add a ration to the config such as `3:2:4`, which will define the size of the different widgets. See: [CSS-Flex ](https://developer.mozilla.org/en-US/docs/Web/CSS/flex)

#### Pros 
- One value to define the size of all relevant widgets
- Errors can be entirely avoided 
- Easier to scale to different places in the app 

#### Cons 
- A tiny bit harder to understand 
- Will require adding a new "type" to the config 



Discussion / hints:

Fix patch:
diff --git a/docs/config.md b/docs/config.md
index d8337f9a..5110b2a8 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -7,6 +7,7 @@
   - [Media control](#media-control)
   - [Player event hook command](#player-event-hook-command)
   - [Device configurations](#device-configurations)
+  - [Layout configurations](#layout-configurations)
 - [Themes](#themes)
   - [Use script to add theme](#use-script-to-add-theme)
   - [Palette](#palette)
@@ -14,7 +15,7 @@
 - [Keymaps](#keymaps)
 
 All configuration files should be placed inside the application's configuration folder (default to be `$HOME/.config/spotify-player`).
-
+    
 ## General
 
 **The default `app.toml` can be found in the example [`app.toml`](../examples/app.toml) file.**
@@ -26,7 +27,7 @@ All configuration files should be placed inside the application's configuration
 | `client_id`                       | the Spotify client's ID                                                                  | `65b708073fc0480ea92a077233ca87bd`                      |
 | `client_port`                     | the port that the application's client is running on to handle CLI commands              | `8080`                                                  |
 | `tracks_playback_limit`           | the limit for the number of tracks played in a **tracks** playback                       | `50`                                                    |
-| `playback_format`                 | the format of the text in the playback's window                                          | `{status} {track} • {artists}\n{album}\n{metadata}`              |
+| `playback_format`                 | the format of the text in the playback's window                                          | `{status} {track} • {artists}\n{album}\n{metadata}`     | 
 | `notify_format`                   | the format of a notification (`notify` feature only)                                     | `{ summary = "{track} • {artists}", body = "{album}" }` |
 | `notify_timeout_in_secs`          | the timeout (in seconds) of a notification (`notify` feature only)                       | `0` (no timeout)                                        |
 | `player_event_hook_command`       | the hook command executed when there is a new player event                               | `None`                                                  |
@@ -42,13 +43,11 @@ All configuration files should be placed inside the application's configuration
 | `enable_cover_image_cache`        | store album's cover images in the cache folder                                           | `true`                                                  |
 | `notify_streaming_only`           | only send notification when streaming is enabled (`streaming` and `notify` feature only) | `false`                                                 |
 | `default_device`                  | the default device to connect to on startup if no playing device found                   | `spotify-player`                                        |
-| `play_icon`                       | the icon to indicate playing state of a Spotify item                                     | `▶`                                                    |
+| `play_icon`                       | the icon to indicate playing state of a Spotify item                                     | `▶`                                                     |
 | `pause_icon`                      | the icon to indicate pause state of a Spotify item                                       | `▌▌`                                                    |
-| `liked_icon`                      | the icon to indicate the liked state of a song                                           | `♥`                                                    |
+| `liked_icon`                      | the icon to indicate the liked state of a song                                           | `♥`                                                     |
 | `border_type`                     | the type of the application's borders                                                    | `Plain`                                                 |
 | `progress_bar_type`               | the type of the playback progress bar                                                    | `Rectangle`                                             |
-| `playback_window_position`        | the position of the playback window                                                      | `Top`                                                   |
-| `playback_window_width`           | the width of the playback window                                                         | `6`                                                     |
 | `cover_img_width`                 | the width of the cover image (`image` feature only)                                      | `5`                                                     |
 | `cover_img_length`                | the length of the cover image (`image` feature only)                                     | `9`                                                     |
 | `cover_img_scale`                 | the scale of the cover image (`image` feature only)                                      | `1.0`                                                   |
@@ -137,6 +136,26 @@ The configuration options for the [Librespot](https://github.com/librespot-org/l
 
 More details on the above configuration options can be found under the [Librespot wiki page](https://github.com/librespot-org/librespot/wiki/Options).
 
+### Layout configurations
+
+The layout of the application can be adjusted via these options. 
+
+| Option                     | Description                                                      | Default |
+| -------------------------- | ---------------------------------------------------------------- | ------- |
+| `library.album_percent`    | The percentage of the album window in the library                | `40`    |
+| `library.playlist_percent` | The percentage of the playlist window in the library             | `40`    |
+| `playback_window_position` | The position of the playback window                              | `Top`   |
+| `playback_window_height`   | The height of the playback window                                | `6`     |
+
+Example: 
+
+``` toml
+
+[layout]
+library = { album_percent = 40, playlist_percent = 40 }
+playback_window_position = "Top"
+
+```
 ## Themes
 
 `spotify_player` uses the `theme.toml` config file to look for user-defined themes.
diff --git a/examples/app.toml b/examples/app.toml
index c173df96..7c055ae7 100644
--- a/examples/app.toml
+++ b/examples/app.toml
@@ -17,10 +17,8 @@ default_device = "spotify-player"
 play_icon = "▶"
 pause_icon = "▌▌"
 liked_icon = "♥"
-playback_window_position = "Top"
 cover_img_length = 9
 cover_img_width = 5
-playback_window_width = 6
 seek_duration_secs = 5
 
 [device]
@@ -31,3 +29,8 @@ bitrate = 320
 audio_cache = false
 normalization = false
 autoplay = false
+
+[layout]
+library = { playlist_percent = 40, album_percent = 40 }
+playback_window_position = "Top"
+playback_window_height = 6
diff --git a/spotify_player/src/config/mod.rs b/spotify_player/src/config/mod.rs
index 074deab0..36f91e4c 100644
--- a/spotify_player/src/config/mod.rs
+++ b/spotify_player/src/config/mod.rs
@@ -80,7 +80,7 @@ pub struct AppConfig {
     pub border_type: BorderType,
     pub progress_bar_type: ProgressBarType,
 
-    pub playback_window_position: Position,
+    pub layout: LayoutConfig,
 
     #[cfg(feature = "image")]
     pub cover_img_length: usize,
@@ -89,8 +89,6 @@ pub struct AppConfig {
     #[cfg(feature = "image")]
     pub cover_img_scale: f32,
 
-    pub playback_window_width: usize,
-
     #[cfg(feature = "media-control")]
     pub enable_media_control: bool,
 
@@ -162,6 +160,20 @@ pub struct NotifyFormat {
     pub body: String,
 }
 
+#[derive(Debug, Deserialize, Serialize, ConfigParse, Clone)]
+// Application layout configurations
+pub struct LayoutConfig {
+    pub library: LibraryLayoutConfig,
+    pub playback_window_position: Position,
+    pub playback_window_height: usize,
+}
+
+#[derive(Debug, Deserialize, Serialize, ConfigParse, Clone)]
+pub struct LibraryLayoutConfig {
+    pub playlist_percent: u16,
+    pub album_percent: u16,
+}
+
 #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
 #[serde(from = "StreamingTypeOrBool")]
 pub enum StreamingType {
@@ -247,7 +259,7 @@ impl Default for AppConfig {
             border_type: BorderType::Plain,
             progress_bar_type: ProgressBarType::Rectangle,
 
-            playback_window_position: Position::Top,
+            layout: LayoutConfig::default(),
 
             #[cfg(feature = "image")]
             cover_img_length: 9,
@@ -256,8 +268,6 @@ impl Default for AppConfig {
             #[cfg(feature = "image")]
             cover_img_scale: 1.0,
 
-            playback_window_width: 6,
-
             // Because of the "creating new window and stealing focus" behaviour
             // when running the media control event loop on startup,
             // media control support is disabled by default for Windows and MacOS.
@@ -303,6 +313,29 @@ impl Default for DeviceConfig {
     }
 }
 
+impl Default for LayoutConfig {
+    fn default() -> Self {
+        Self {
+            library: LibraryLayoutConfig {
+                playlist_percent: 40,
+                album_percent: 40,
+            },
+            playback_window_position: Position::Top,
+            playback_window_height: 6,
+        }
+    }
+}
+
+impl LayoutConfig {
+    fn check_values(&self) -> anyhow::Result<()> {
+        if self.library.album_percent + self.library.playlist_percent > 99 {
+            anyhow::bail!("Invalid library layout: summation of album_percent and playlist_percent cannot be greater than 99!");
+        } else {
+            Ok(())
+        }
+    }
+}
+
 impl AppConfig {
     pub fn new(path: &Path) -> Result<Self> {
         let mut config = Self::default();
@@ -310,6 +343,7 @@ impl AppConfig {
             config.write_config_file(path)?
         }
 
+        config.layout.check_values()?;
         Ok(config)
     }
 
diff --git a/spotify_player/src/ui/page.rs b/spotify_player/src/ui/page.rs
index 2053a07c..9af3756d 100644
--- a/spotify_player/src/ui/page.rs
+++ b/spotify_player/src/ui/page.rs
@@ -315,6 +315,7 @@ pub fn render_library_page(
     // 1. Get data
     let curr_context_uri = state.player.read().playing_context_id().map(|c| c.uri());
     let data = state.data.read();
+    let configs = config::get_config();
 
     let focus_state = match ui.current_page() {
         PageState::Library { state } => state.focus,
@@ -326,12 +327,17 @@ pub fn render_library_page(
     // - a playlists window
     // - a saved albums window
     // - a followed artists window
+
     let chunks = Layout::horizontal([
-        Constraint::Percentage(40),
-        Constraint::Percentage(40),
-        Constraint::Percentage(20),
+        Constraint::Percentage(configs.app_config.layout.library.playlist_percent),
+        Constraint::Percentage(configs.app_config.layout.library.album_percent),
+        Constraint::Percentage(
+            100 - (configs.app_config.layout.library.album_percent
+                + configs.app_config.layout.library.playlist_percent),
+        ),
     ])
     .split(rect);
+
     let playlist_rect = construct_and_render_block(
         "Playlists",
         &ui.theme,
diff --git a/spotify_player/src/ui/playback.rs b/spotify_player/src/ui/playback.rs
index 6957882d..ff6f2360 100644
--- a/spotify_player/src/ui/playback.rs
+++ b/spotify_player/src/ui/playback.rs
@@ -333,7 +333,7 @@ fn render_playback_cover_image(state: &SharedState, ui: &mut UIStateGuard) -> Re
 /// and the second one for the main application's layout (popup, page, etc).
 fn split_rect_for_playback_window(rect: Rect) -> (Rect, Rect) {
     let configs = config::get_config();
-    let playback_width = configs.app_config.playback_window_width;
+    let playback_width = configs.app_config.layout.playback_window_height;
     // the playback window's width should not be smaller than the cover image's width + 1
     #[cfg(feature = "image")]
     let playback_width = std::cmp::max(configs.app_config.cover_img_width + 1, playback_width);
@@ -341,7 +341,7 @@ fn split_rect_for_playback_window(rect: Rect) -> (Rect, Rect) {
     // +2 for top/bottom borders
     let playback_width = (playback_width + 2) as u16;
 
-    match configs.app_config.playback_window_position {
+    match configs.app_config.layout.playback_window_position {
         config::Position::Top => {
             let chunks =
                 Layout::vertical([Constraint::Length(playback_width), Constraint::Fill(0)])


Test patch: