Started work on the playlist creation dialog. To facilitate my desired UX for dialogs, I changed the immediate child of KotoWindow to being a GtkOverlay, with the "child" being the primary layout GtkBox and the "overlay" being any active dialog. There is no background on the dialogs yet, or really anything meaningful in it besides the GtkImage to show the playlist image as well as a GtkEntry for the label. Upon clicking the image, you will be presented with a GtkFileChooserNative, which will allow us to support various platforms aside from Linux (if we desire) but more importantly support desktop portals and the appropriate picker for each desktop environment. This dialog is currently hooked into the + button in the Playlist nav section. The intent is to also support drag-and-drop when possible, so you can drag an image file onto the GtkImage for it to set it to the playlist image. Fixed various compiler warnings, such as: - Unused variables (cast these as void so compiler knows to ignore them). - Use g_hash_table_add instead of g_hash_table_insert since value does not matter and will complain about not casting TRUE as a pointer. - Fixed some casting. Fixed up Desktop file and fleshed out Appstream data. Use validate-relax in the appstream test or it gets cranky about screenshots missing. I know...the app is not ready yet. Get over it AppStream. Added Visual Studio Code tasks and some C/C++ Extension setting files.
186 lines
7 KiB
C
186 lines
7 KiB
C
/* koto-window.c
|
|
*
|
|
* Copyright 2021 Joshua Strobl
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <gtk-4.0/gdk/x11/gdkx.h>
|
|
#include "indexer/structs.h"
|
|
#include "pages/music/music-local.h"
|
|
#include "playback/engine.h"
|
|
#include "playlist/current.h"
|
|
#include "playlist/create-dialog.h"
|
|
#include "koto-config.h"
|
|
#include "koto-nav.h"
|
|
#include "koto-playerbar.h"
|
|
#include "koto-window.h"
|
|
|
|
extern KotoCurrentPlaylist *current_playlist;
|
|
extern KotoPlaybackEngine *playback_engine;
|
|
|
|
struct _KotoWindow {
|
|
GtkApplicationWindow parent_instance;
|
|
KotoIndexedLibrary *library;
|
|
KotoCurrentPlaylist *current_playlist;
|
|
|
|
KotoCreatePlaylistDialog *playlist_create_dialog;
|
|
|
|
GtkWidget *overlay;
|
|
GtkWidget *header_bar;
|
|
GtkWidget *menu_button;
|
|
GtkWidget *search_entry;
|
|
|
|
GtkWidget *primary_layout;
|
|
GtkWidget *content_layout;
|
|
|
|
KotoNav *nav;
|
|
GtkWidget *pages;
|
|
KotoPlayerBar *player_bar;
|
|
};
|
|
|
|
G_DEFINE_TYPE (KotoWindow, koto_window, GTK_TYPE_APPLICATION_WINDOW)
|
|
|
|
static void koto_window_class_init (KotoWindowClass *klass) {
|
|
(void)klass;
|
|
}
|
|
|
|
static void koto_window_init (KotoWindow *self) {
|
|
current_playlist = koto_current_playlist_new();
|
|
playback_engine = koto_playback_engine_new();
|
|
|
|
GtkCssProvider* provider = gtk_css_provider_new();
|
|
gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css");
|
|
gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
|
|
create_new_headerbar(self); // Create our headerbar
|
|
|
|
self->overlay = gtk_overlay_new(); // Create our overlay
|
|
self->playlist_create_dialog = koto_create_playlist_dialog_new(); // Create our Create Playlist dialog
|
|
|
|
self->primary_layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_widget_add_css_class(self->primary_layout, "primary-layout");
|
|
gtk_widget_set_hexpand(self->primary_layout, TRUE);
|
|
gtk_widget_set_vexpand(self->primary_layout, TRUE);
|
|
|
|
gtk_overlay_set_child(GTK_OVERLAY(self->overlay), self->primary_layout); // Add our primary layout to the overlay
|
|
|
|
self->content_layout = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_widget_add_css_class(self->content_layout, "content-layout");
|
|
gtk_widget_set_hexpand(self->content_layout, TRUE);
|
|
gtk_widget_set_vexpand(self->content_layout, TRUE);
|
|
|
|
self->nav = koto_nav_new();
|
|
|
|
if (self->nav != NULL) {
|
|
gtk_box_prepend(GTK_BOX(self->content_layout), koto_nav_get_nav(self->nav));
|
|
}
|
|
|
|
self->pages = gtk_stack_new(); // New stack to hold our pages
|
|
|
|
if (GTK_IS_STACK(self->pages)) { // Created our stack successfully
|
|
gtk_stack_set_transition_type(GTK_STACK(self->pages), GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT);
|
|
gtk_box_append(GTK_BOX(self->content_layout), self->pages);
|
|
gtk_widget_set_hexpand(self->pages, TRUE);
|
|
gtk_widget_set_vexpand(self->pages, TRUE);
|
|
}
|
|
|
|
gtk_box_prepend(GTK_BOX(self->primary_layout), self->content_layout);
|
|
|
|
self->player_bar = koto_playerbar_new();
|
|
|
|
if (self->player_bar != NULL) {
|
|
GtkWidget *playerbar_main = koto_playerbar_get_main(self->player_bar);
|
|
gtk_box_append(GTK_BOX(self->primary_layout), playerbar_main);
|
|
}
|
|
|
|
gtk_window_set_child(GTK_WINDOW(self), self->overlay);
|
|
#ifdef GDK_WINDOWING_X11
|
|
set_optimal_default_window_size(self);
|
|
#endif
|
|
gtk_window_set_title(GTK_WINDOW(self), "Koto");
|
|
gtk_window_set_icon_name(GTK_WINDOW(self), "audio-headphones");
|
|
gtk_window_set_startup_id(GTK_WINDOW(self), "com.github.joshstrobl.koto");
|
|
|
|
gtk_widget_queue_draw(self->content_layout);
|
|
g_thread_new("load-library", (void*) load_library, self);
|
|
}
|
|
|
|
void create_new_headerbar(KotoWindow *self) {
|
|
self->header_bar = gtk_header_bar_new();
|
|
gtk_widget_add_css_class(self->header_bar, "hdr");
|
|
g_return_if_fail(GTK_IS_HEADER_BAR(self->header_bar));
|
|
|
|
self->menu_button = gtk_button_new_from_icon_name("audio-headphones");
|
|
|
|
self->search_entry = gtk_search_entry_new();
|
|
gtk_widget_add_css_class(self->menu_button, "flat");
|
|
gtk_widget_set_can_focus(self->search_entry, TRUE);
|
|
gtk_widget_set_size_request(self->search_entry, 400, -1); // Have 400px width
|
|
g_object_set(self->search_entry, "placeholder-text", "Search...", NULL);
|
|
|
|
gtk_header_bar_pack_start(GTK_HEADER_BAR(self->header_bar), self->menu_button);
|
|
gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(self->header_bar), TRUE);
|
|
gtk_header_bar_set_title_widget(GTK_HEADER_BAR(self->header_bar), self->search_entry);
|
|
|
|
gtk_window_set_titlebar(GTK_WINDOW(self), self->header_bar);
|
|
}
|
|
|
|
void koto_window_hide_create_playlist_dialog(KotoWindow *self) {
|
|
gtk_overlay_remove_overlay(GTK_OVERLAY(self->overlay), koto_create_playlist_dialog_get_content(self->playlist_create_dialog));
|
|
}
|
|
|
|
void koto_window_show_create_playlist_dialog(KotoWindow *self) {
|
|
gtk_overlay_add_overlay(GTK_OVERLAY(self->overlay), koto_create_playlist_dialog_get_content(self->playlist_create_dialog));
|
|
}
|
|
|
|
void load_library(KotoWindow *self) {
|
|
KotoIndexedLibrary *lib = koto_indexed_library_new(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
|
|
|
|
if (lib != NULL) {
|
|
self->library = lib;
|
|
KotoPageMusicLocal* l = koto_page_music_local_new();
|
|
|
|
// TODO: Remove and do some fancy state loading
|
|
gtk_stack_add_named(GTK_STACK(self->pages), GTK_WIDGET(l), "music.local");
|
|
gtk_stack_set_visible_child_name(GTK_STACK(self->pages), "music.local");
|
|
gtk_widget_show(self->pages); // Do not remove this. Will cause sporadic hiding of the local page content otherwise.
|
|
koto_page_music_local_set_library(l, self->library);
|
|
}
|
|
|
|
g_thread_exit(0);
|
|
}
|
|
|
|
void set_optimal_default_window_size(KotoWindow *self) {
|
|
GdkDisplay *default_display = gdk_display_get_default();
|
|
g_return_if_fail(GDK_IS_X11_DISPLAY(default_display));
|
|
|
|
GdkMonitor *default_monitor = gdk_x11_display_get_primary_monitor(GDK_X11_DISPLAY(default_display)); // Get primary monitor for the X11
|
|
g_return_if_fail(default_monitor);
|
|
|
|
|
|
GdkRectangle workarea = {0};
|
|
gdk_monitor_get_geometry(default_monitor, &workarea);
|
|
|
|
if (workarea.width <= 1280) { // Honestly how do you even get anything done?
|
|
gtk_widget_set_size_request(GTK_WIDGET(self), 1200, 675);
|
|
} else if ((workarea.width > 1280) && (workarea.width <= 1600)) { // Plebian monitor resolution
|
|
gtk_widget_set_size_request(GTK_WIDGET(self), 1300, 709);
|
|
} else if ((workarea.width > 1600) && (workarea.width <= 1920)) { // Something slightly normal
|
|
gtk_widget_set_size_request(GTK_WIDGET(self), 1600, 900);
|
|
} else if ((workarea.width > 1920) && (workarea.width <= 2560)) { // Well aren't you hot stuff?
|
|
gtk_widget_set_size_request(GTK_WIDGET(self), 1920, 1080);
|
|
} else { // Now you're just flexing
|
|
gtk_widget_set_size_request(GTK_WIDGET(self), 2560, 1440);
|
|
}
|
|
}
|