From b0120692692114e565c40ed884630524011559c0 Mon Sep 17 00:00:00 2001 From: Zachary Hall Date: Mon, 17 Jul 2023 12:21:38 -0700 Subject: [PATCH] Add portal support and update .gitignore --- .gitignore | 5 +- file_browser.cpp | 175 ++++++++++++++++++++++++++++++++++++++++++++++ file_browser.h | 51 ++++++++++++++ main.cpp | 22 ++++-- meson.build | 20 ++++-- meson.options | 1 - meson_options.txt | 1 + theme.cpp | 9 ++- theme.h | 2 +- 9 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 file_browser.cpp create mode 100644 file_browser.h delete mode 100644 meson.options diff --git a/.gitignore b/.gitignore index 1eef674..d45ad1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,9 @@ assets/*.h build* .vscode -subprojects/*/ .cache compile_commands.json -!subprojects/SDL-Mixer-X/ -!subprojects/jsoncpp/ .flatpak-builder flatpak-repo *.flatpak -!build-file.sh +!build*.sh diff --git a/file_browser.cpp b/file_browser.cpp new file mode 100644 index 0000000..e54116e --- /dev/null +++ b/file_browser.cpp @@ -0,0 +1,175 @@ +#include "file_browser.h" +#include "imfilebrowser.h" + + +FileBrowser::FileBrowser(bool save) { + #ifdef PORTALS + g_set_application_name("Neko Player"); + main_context = g_main_context_default(); + main_loop = g_main_loop_new(main_context, true); + portal = xdp_portal_new(); + type = g_variant_type_new("a(sa(us))"); + inner_filter_type = g_variant_type_new("a(us)"); + #endif + this->save = save; + fallback = ImGui::FileBrowser(save ? ImGuiFileBrowserFlags_CreateNewDir|ImGuiFileBrowserFlags_EnterNewFilename : 0); +} +void FileBrowser::SetTypeFilters(string name, vector filters) { + filter_name = name; + this->filters = filters; + #ifdef PORTALS + if (variant != NULL) { + g_variant_unref(variant); + } + GVariantBuilder builder; + GVariantBuilder inner_filter_builder; + g_variant_builder_init(&builder, type); + g_variant_builder_init(&inner_filter_builder, inner_filter_type); + GVariant *name_variant = g_variant_new_string((const gchar*)name.c_str()); + for (auto filter : filters) { + GVariant *id = g_variant_new_uint32(0); + GVariant *filter_variant = g_variant_new_string((const gchar*)(string("*") + filter).c_str()); + GVariant **filter_children = new GVariant*[2] {id, filter_variant}; + GVariant *filter_tuple = g_variant_new_tuple(filter_children, 2); + g_variant_builder_add_value(&inner_filter_builder, filter_tuple); + } + GVariant *inner_filters = g_variant_builder_end(&inner_filter_builder); + GVariant **outer_filter_children = new GVariant*[2] {name_variant, inner_filters}; + GVariant *outer_filter_tuple = g_variant_new_tuple(outer_filter_children, 2); + g_variant_builder_add_value(&builder, outer_filter_tuple); + variant = g_variant_builder_end(&builder); + g_variant_ref(variant); + #endif + fallback.SetTypeFilters(filters); +} +void FileBrowser::SetPwd(path path) { + pwd = path; + #ifndef PORTALS + fallback.SetPwd(path); + #endif +} +bool FileBrowser::HasSelected() { + #ifdef PORTALS + return selected.has_value(); + #else + return fallback.HasSelected(); + #endif +} +path FileBrowser::GetSelected() { + #ifdef PORTALS + return selected.value_or(path()); + #else + return fallback.GetSelected(); + #endif +} +void FileBrowser::SetWindowSize(int w, int h) { + fallback.SetWindowSize(w, h); +} +void FileBrowser::SetWindowPos(int x, int y) { + fallback.SetWindowPos(x, y); +} +void FileBrowser::Open() { + #ifdef PORTALS + open = true; + if (save) { + xdp_portal_save_file(portal, NULL, title.c_str(), "", pwd.c_str(), "", variant, NULL, NULL, XDP_SAVE_FILE_FLAG_NONE, NULL, &FileBrowser::FileBrowserSaveCallback, this); + } else { + xdp_portal_open_file(portal, NULL, title.c_str(), variant, NULL, NULL, XDP_OPEN_FILE_FLAG_NONE, NULL, &FileBrowser::FileBrowserOpenCallback, this); + } + #else + fallback.Open(); + #endif +} +#ifdef PORTALS +void FileBrowser::FileBrowserOpenCallback(GObject *src, GAsyncResult *res, gpointer data) { + (void)src; + FileBrowser *self = (FileBrowser*)data; + GVariant *variant = xdp_portal_open_file_finish(self->portal, res, NULL); + if (variant == NULL) { + printf("Cancelled.\n"); + return; + } + GVariant *uris = g_variant_lookup_value(variant, "uris", G_VARIANT_TYPE_STRING_ARRAY); + GVariant *first_uri = g_variant_get_child_value(uris, 0); + gsize length; + const gchar* c_str_without_terminater = g_variant_get_string(first_uri, &length); + vector c_str_vec; + c_str_vec.reserve(length + 1); + for (gsize i = 0; i < length; i++) { + c_str_vec.push_back(c_str_without_terminater[i]); + } + c_str_vec.push_back('\0'); + const char *c_str = c_str_vec.data(); + printf("Selected file %s\n", c_str); + string str = c_str; + if (str.starts_with("file://")) { + str = str.substr(string("file://").length()); + } + self->open = false; + self->selected = path(str); +} + +void FileBrowser::FileBrowserSaveCallback(GObject *src, GAsyncResult *res, gpointer data) { + (void)src; + FileBrowser *self = (FileBrowser*)data; + self->open = false; + GVariant *variant = xdp_portal_save_file_finish(self->portal, res, NULL); + if (variant == NULL) { + printf("Variant was null.\n"); + return; + } + GVariant *first_index = g_variant_get_child_value(variant, 0); + if (first_index == NULL) { + printf("Variant had no first index.\n"); + return; + } + const char *c_str = (const char*)g_variant_get_bytestring(first_index); + string str = c_str; + if (str.starts_with("file://")) { + str = str.substr(string("file://").length()); + } + self->selected = path(str); +} +#endif + +void FileBrowser::Display() { + #ifdef PORTALS + g_main_context_iteration(main_context, false); + #else + fallback.Display() + #endif +} + +void FileBrowser::ClearSelected() { + selected = optional(); + #ifndef PORTALS + fallback.ClearSelected(); + #endif +} +void FileBrowser::SetTitle(string title) { + this->title = title; + #ifndef PORTALS + fallback.SetTitle(title); + #endif +} +bool FileBrowser::IsOpened() { + #ifdef PORTALS + return open; + #else + return fallback.IsOpened(); + #endif +} +FileBrowser::~FileBrowser() { + #ifdef PORTALS + if (variant != NULL) { + g_variant_unref(variant); + } + if (type != NULL) { + g_variant_type_free(type); + } + if (inner_filter_type != NULL) { + g_variant_type_free(inner_filter_type); + } + g_main_loop_quit(main_loop); + #endif +} \ No newline at end of file diff --git a/file_browser.h b/file_browser.h new file mode 100644 index 0000000..af293e9 --- /dev/null +++ b/file_browser.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include +#include +#include +#ifdef PORTALS +#include +#include +#endif +#include "imgui.h" +#include "imfilebrowser.h" + +using std::string; +using std::vector; +using std::optional; +using namespace std::filesystem; + +class FileBrowser { + #ifdef PORTALS + XdpPortal *portal; + GVariant *variant = NULL; + GVariantType *type = NULL; + GVariantType *inner_filter_type = NULL; + GMainLoop *main_loop; + GMainContext *main_context; + static void FileBrowserOpenCallback(GObject *src, GAsyncResult *res, gpointer data); + static void FileBrowserSaveCallback(GObject *src, GAsyncResult *res, gpointer data); + bool open = false; + #endif + bool save; + ImGui::FileBrowser fallback; + path pwd; + string filter_name; + vector filters; + string title; + optional selected; + public: + void SetTitle(string title); + void SetTypeFilters(string name, vector filters); + void SetPwd(path path); + bool HasSelected(); + path GetSelected(); + void SetWindowSize(int width, int height); + void SetWindowPos(int x, int y); + void Display(); + void Open(); + void ClearSelected(); + bool IsOpened(); + FileBrowser(bool save); + ~FileBrowser(); +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index acf32ec..5e3dbc7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,7 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" #include "imgui_impl_opengl3.h" -#include "imfilebrowser.h" +#include "file_browser.h" #include "playback.h" #include "theme.h" #include "icon.h" @@ -177,9 +177,9 @@ int main(int, char**) bool show_demo_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - ImGui::FileBrowser fileDialog; + FileBrowser fileDialog(false); fileDialog.SetTitle("Open..."); - fileDialog.SetTypeFilters({ ".wav", ".ogg", ".mp3", ".qoa", ".flac", ".xm", ".mod"}); + fileDialog.SetTypeFilters("Audio files", { ".wav", ".ogg", ".mp3", ".qoa", ".flac", ".xm", ".mod"}); std::string userdir = std::getenv( #ifdef _WIN32 "UserProfile" @@ -269,8 +269,16 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; - if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) - done = true; + if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_RESIZED) { + window_width = event.window.data1; + window_height = event.window.data2; + //SDL_GetWindowSize(window, &window_width, &window_height); + } + if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) { + done = true; + } + } } // Start the Dear ImGui frame @@ -398,6 +406,10 @@ int main(int, char**) Theme::ShowEditor(&theme_editor, theme); theme->Apply(accent_color); } + if (fileDialog.IsOpened()) { + fileDialog.SetWindowSize(window_width, window_height); + fileDialog.SetWindowPos(0, 0); + } fileDialog.Display(); if (fileDialog.HasSelected()) { diff --git a/meson.build b/meson.build index bc7e8a2..cac280e 100644 --- a/meson.build +++ b/meson.build @@ -9,27 +9,34 @@ endif if get_option('gles') add_global_arguments('-DIMGUI_IMPL_OPENGL_ES2', language: 'cpp') endif +# SDL Mixer X smx_opts = cmake.subproject_options() smx_opts.add_cmake_defines({'SDL_MIXER_X_STATIC': true, 'SDL_MIXER_X_SHARED': false, 'USE_MIDI_NATIVE_ALT': false, 'USE_MIDI_NATIVE': false}) smx_opts.set_override_option('c_std', 'c99') smx_subproj = cmake.subproject('SDL-Mixer-X', options: smx_opts) -#raudio_lib = static_library('raudio', 'raudio/src/raudio.c', c_args: ['-DRAUDIO_STANDALONE', '-DSUPPORT_MODULE_RAUDIO', '-DSUPPORT_FILEFORMAT_WAV', '-DSUPPORT_FILEFORMAT_OGG', '-DSUPPORT_FILEFORMAT_MP3', '-DSUPPORT_FILEFORMAT_QOA', '-DSUPPORT_FILEFORMAT_FLAC', '-DSUPPORT_FILEFORMAT_XM', '-DSUPPORT_FILEFORMAT_MOD', '-w']) -#raudio_include = include_directories('raudio/src') -#raudio_dep = declare_dependency(include_directories: raudio_include, link_with: raudio_lib) + +opts = [] + deps = [ dependency('sdl2'), dependency('SDL2_image'), dependency('gl'), subproject('jsoncpp').get_variable('jsoncpp_dep'), dependency('soundtouch'), - #raudio_dep, - smx_subproj.dependency('SDL2_mixer_ext_Static'), + smx_subproj.dependency('SDL2_mixer_ext_Static') ] +if get_option('portals') and host_machine.system() == 'linux' + # Dbus CXX + deps += dependency('libportal') + opts += '-DPORTALS' +endif + srcs = [ 'main.cpp', 'playback.cpp', 'theme.cpp', + 'file_browser.cpp', 'imgui/imgui.cpp', 'imgui/imgui_widgets.cpp', 'imgui/imgui_tables.cpp', @@ -66,4 +73,5 @@ install_data('assets/com.experimentalcraft.NekoPlayer.metainfo.xml', install_dir exe = executable('neko-player', srcs, dependencies: deps, include_directories: include_dirs, - install : true) + install : true, + cpp_args: opts) diff --git a/meson.options b/meson.options deleted file mode 100644 index 67886f5..0000000 --- a/meson.options +++ /dev/null @@ -1 +0,0 @@ -option('gles', type: 'boolean', value: false) diff --git a/meson_options.txt b/meson_options.txt index 67886f5..3f5dc06 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('gles', type: 'boolean', value: false) +option('portals', type: 'boolean', value: true) \ No newline at end of file diff --git a/theme.cpp b/theme.cpp index 53e80ee..975b1e5 100644 --- a/theme.cpp +++ b/theme.cpp @@ -27,8 +27,8 @@ ImVec4 change_accent_color(ImVec4 in, float hue) { return out; } bool Theme::ShowEditor(bool* open, Theme* &theme) { - static ImGui::FileBrowser importDialog; - static ImGui::FileBrowser exportDialog; + static FileBrowser importDialog(false); + static FileBrowser exportDialog(true); static bool loadOpen = false; static bool saveAsOpen = false; ImGui::Begin("Theme Editor", open); @@ -54,7 +54,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) { } if (ImGui::Button("Import...", buttonSize)) { importDialog.SetTitle("Import theme..."); - importDialog.SetTypeFilters({ ".json"}); + importDialog.SetTypeFilters("Theme JSON files", { ".json"}); std::string userdir = std::getenv( #ifdef _WIN32 "UserProfile" @@ -67,9 +67,8 @@ bool Theme::ShowEditor(bool* open, Theme* &theme) { } ImGui::SameLine(); if (ImGui::Button("Export...", buttonSize)) { - exportDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename|ImGuiFileBrowserFlags_CreateNewDir); exportDialog.SetTitle("Export theme..."); - exportDialog.SetTypeFilters({ ".json"}); + exportDialog.SetTypeFilters("Theme JSON files", { ".json"}); std::string userdir = std::getenv( #ifdef _WIN32 "UserProfile" diff --git a/theme.h b/theme.h index 3a7cec1..4340d6d 100644 --- a/theme.h +++ b/theme.h @@ -2,7 +2,7 @@ #include "imgui.h" #include #include -#include "imfilebrowser.h" +#include "file_browser.h" #include using std::string; using namespace std::filesystem;