From 28b18aa67b1e1cc1a2e9488a14eee782ba5d100d Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sun, 15 May 2022 18:42:33 +0300 Subject: [PATCH] Project migrated from gtkmm to plain gtk. Main window now loading from xml (located in resources) --- CMakeLists.txt | 44 +++- cmake/Modules/BuildTargetScript.cmake | 57 +++++ cmake/Modules/CompileGResources.cmake | 229 ++++++++++++++++++ cmake/Modules/GenerateGXML.cmake | 124 ++++++++++ .../Modules/GlibCompileResourcesSupport.cmake | 11 + coro/GLibMainContextExecutor.cpp | 2 +- gtkpp/Application.cpp | 9 +- gtkpp/Application.h | 4 +- gtkpp/Leaflet.cpp | 18 ++ gtkpp/Leaflet.h | 20 ++ gtkpp/MessageDialog.cpp | 21 ++ gtkpp/MessageDialog.h | 19 ++ gtkpp/Window.cpp | 67 +++-- gtkpp/Window.h | 15 +- gui/LoginWindow.cpp | 44 ++-- gui/LoginWindow.h | 10 +- gui/MainWindow.cpp | 37 +-- gui/MainWindow.h | 21 +- gui/MainWindow.xml | 54 +++++ gui/TitleBar.cpp | 21 -- gui/TitleBar.h | 23 -- main.cpp | 44 +--- 22 files changed, 704 insertions(+), 190 deletions(-) create mode 100644 cmake/Modules/BuildTargetScript.cmake create mode 100644 cmake/Modules/CompileGResources.cmake create mode 100644 cmake/Modules/GenerateGXML.cmake create mode 100644 cmake/Modules/GlibCompileResourcesSupport.cmake create mode 100644 gtkpp/Leaflet.cpp create mode 100644 gtkpp/Leaflet.h create mode 100644 gtkpp/MessageDialog.cpp create mode 100644 gtkpp/MessageDialog.h create mode 100644 gui/MainWindow.xml delete mode 100644 gui/TitleBar.cpp delete mode 100644 gui/TitleBar.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f41788b..3b2d572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(autocat_gnome) set(CMAKE_CXX_STANDARD 20) +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g -Og") if(APPLE) @@ -12,20 +13,20 @@ find_package(PkgConfig REQUIRED) find_package(nlohmann_json REQUIRED) find_package(folly REQUIRED) -pkg_check_modules(GTKMM REQUIRED gtkmm-4.0) -pkg_check_modules(GLIBMM REQUIRED glibmm-2.68) +pkg_check_modules(GTK REQUIRED gtk4) +pkg_check_modules(GLIB REQUIRED glib-2.0) pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4) pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1) pkg_check_modules(LIBSIGCPP REQUIRED sigc++-3.0) -include_directories(${GTKMM_INCLUDE_DIRS} - ${GLIBMM_INCLUDE_DIRS} +include_directories(${GTK_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS} ${LIBADWAITA_INCLUDE_DIRS} ${LIBSIGCPP_INCLUDE_DIRS}) -link_directories(${GTKMM_LIBRARY_DIRS} - ${GLIBMM_LIBRARY_DIRS} +link_directories(${GTK_LIBRARY_DIRS} + ${GLIB_LIBRARY_DIRS} ${LIBSOUP_LIBRARY_DIRS} ${LIBADWAITA_LIBRARY_DIRS} ${LIBSIGCPP_LIBRARY_DIRS}) @@ -41,20 +42,41 @@ add_executable(autocat_gnome main.cpp models/User.h services/Settings.cpp services/Settings.h - gui/TitleBar.cpp - gui/TitleBar.h coro/Coro.h coro/GLibMainContextExecutor.cpp coro/GLibMainContextExecutor.h gtkpp/Application.cpp gtkpp/Application.h gtkpp/Window.cpp - gtkpp/Window.h gtkpp/Box.cpp gtkpp/Box.h gtkpp/Widget.cpp gtkpp/Widget.h gtkpp/Entry.cpp gtkpp/Entry.h gtkpp/Button.cpp gtkpp/Button.h gtkpp/Spinner.cpp gtkpp/Spinner.h gtkpp/HeaderBar.cpp gtkpp/HeaderBar.h) + gtkpp/Window.h + gtkpp/Box.cpp + gtkpp/Box.h + gtkpp/Widget.cpp + gtkpp/Widget.h + gtkpp/Entry.cpp + gtkpp/Entry.h + gtkpp/Button.cpp + gtkpp/Button.h + gtkpp/Spinner.cpp + gtkpp/Spinner.h + gtkpp/HeaderBar.cpp + gtkpp/HeaderBar.h + gtkpp/MessageDialog.cpp + gtkpp/MessageDialog.h + gtkpp/Leaflet.cpp + gtkpp/Leaflet.h) -target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES} - ${GLIBMM_LIBRARIES} +target_link_libraries(autocat_gnome ${GTK_LIBRARIES} + ${GLIB_LIBRARIES} ${LIBSOUP_LIBRARIES} ${LIBADWAITA_LIBRARIES} ${LIBSIGCPP_LIBRARIES} nlohmann_json::nlohmann_json Folly::folly) + +set(XML gui/MainWindow.xml) + +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") +include(GlibCompileResourcesSupport) +compile_gresources(RESOURCE_FILE XML_OUT TYPE BUNDLE RESOURCES ${XML} COMPRESS_ALL) +add_custom_target(resource ALL DEPENDS ${RESOURCE_FILE}) diff --git a/cmake/Modules/BuildTargetScript.cmake b/cmake/Modules/BuildTargetScript.cmake new file mode 100644 index 0000000..7243449 --- /dev/null +++ b/cmake/Modules/BuildTargetScript.cmake @@ -0,0 +1,57 @@ +# This file is used to be invoked at build time. It generates the needed +# resource XML file. + +# Input variables that need to provided when invoking this script: +# GXML_OUTPUT The output file path where to save the XML file. +# GXML_COMPRESS_ALL Sets all COMPRESS flags in all resources in resource +# list. +# GXML_NO_COMPRESS_ALL Removes all COMPRESS flags in all resources in +# resource list. +# GXML_STRIPBLANKS_ALL Sets all STRIPBLANKS flags in all resources in +# resource list. +# GXML_NO_STRIPBLANKS_ALL Removes all STRIPBLANKS flags in all resources in +# resource list. +# GXML_TOPIXDATA_ALL Sets all TOPIXDATA flags i nall resources in resource +# list. +# GXML_NO_TOPIXDATA_ALL Removes all TOPIXDATA flags in all resources in +# resource list. +# GXML_PREFIX Overrides the resource prefix that is prepended to +# each relative name in registered resources. +# GXML_RESOURCES The list of resource files. Whether absolute or +# relative path is equal. + +# Include the GENERATE_GXML() function. +include(${CMAKE_CURRENT_LIST_DIR}/GenerateGXML.cmake) + +# Set flags to actual invocation flags. +if(GXML_COMPRESS_ALL) + set(GXML_COMPRESS_ALL COMPRESS_ALL) +endif() +if(GXML_NO_COMPRESS_ALL) + set(GXML_NO_COMPRESS_ALL NO_COMPRESS_ALL) +endif() +if(GXML_STRIPBLANKS_ALL) + set(GXML_STRIPBLANKS_ALL STRIPBLANKS_ALL) +endif() +if(GXML_NO_STRIPBLANKS_ALL) + set(GXML_NO_STRIPBLANKS_ALL NO_STRIPBLANKS_ALL) +endif() +if(GXML_TOPIXDATA_ALL) + set(GXML_TOPIXDATA_ALL TOPIXDATA_ALL) +endif() +if(GXML_NO_TOPIXDATA_ALL) + set(GXML_NO_TOPIXDATA_ALL NO_TOPIXDATA_ALL) +endif() + +# Replace " " with ";" to import the list over the command line. Otherwise +# CMake would interprete the passed resources as a whole string. +string(REPLACE " " ";" GXML_RESOURCES ${GXML_RESOURCES}) + +# Invoke the gresource XML generation function. +generate_gxml(${GXML_OUTPUT} + ${GXML_COMPRESS_ALL} ${GXML_NO_COMPRESS_ALL} + ${GXML_STRIPBLANKS_ALL} ${GXML_NO_STRIPBLANKS_ALL} + ${GXML_TOPIXDATA_ALL} ${GXML_NO_TOPIXDATA_ALL} + PREFIX ${GXML_PREFIX} + RESOURCES ${GXML_RESOURCES}) + diff --git a/cmake/Modules/CompileGResources.cmake b/cmake/Modules/CompileGResources.cmake new file mode 100644 index 0000000..a63aff1 --- /dev/null +++ b/cmake/Modules/CompileGResources.cmake @@ -0,0 +1,229 @@ +include(CMakeParseArguments) + +# Path to this file. +set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# Compiles a gresource resource file from given resource files. Automatically +# creates the XML controlling file. +# The type of resource to generate (header, c-file or bundle) is automatically +# determined from TARGET file ending, if no TYPE is explicitly specified. +# The output file is stored in the provided variable "output". +# "xml_out" contains the variable where to output the XML path. Can be used to +# create custom targets or doing postprocessing. +# If you want to use preprocessing, you need to manually check the existence +# of the tools you use. This function doesn't check this for you, it just +# generates the XML file. glib-compile-resources will then throw a +# warning/error. +function(COMPILE_GRESOURCES output xml_out) + # Available options: + # COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all + # registered resources. + # STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all + # registered resources. + # TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all + # registered resources. + set(CG_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL + STRIPBLANKS_ALL NO_STRIPBLANKS_ALL + TOPIXDATA_ALL NO_TOPIXDATA_ALL) + + # Available one value options: + # TYPE Type of resource to create. Valid options are: + # EMBED_C: A C-file that can be compiled with your project. + # EMBED_H: A header that can be included into your project. + # BUNDLE: Generates a resource bundle file that can be loaded + # at runtime. + # AUTO: Determine from target file ending. Need to specify + # target argument. + # PREFIX Overrides the resource prefix that is prepended to each + # relative file name in registered resources. + # C_PREFIX Specifies the prefix used for the C identifiers in the code generated + # when EMBED_C or EMBED_H are specified for TYPE. + # SOURCE_DIR Overrides the resources base directory to search for resources. + # By default this is set to CMAKE_CURRENT_LIST_DIR. + # TARGET Overrides the name of the output file/-s. Normally the output + # names from the glib-compile-resources tool are taken. + set(CG_ONEVALUEARGS TYPE PREFIX C_PREFIX SOURCE_DIR TARGET) + + # Available multi-value options: + # RESOURCES The list of resource files. Whether absolute or relative path is + # equal, absolute paths are stripped down to relative ones. If the + # absolute path is not inside the given base directory SOURCE_DIR + # or CMAKE_CURRENT_SOURCE_DIR (if SOURCE_DIR is not overriden), + # this function aborts. + # OPTIONS Extra command line options passed to glib-compile-resources. + set(CG_MULTIVALUEARGS RESOURCES OPTIONS) + + # Parse the arguments. + cmake_parse_arguments(CG_ARG + "${CG_OPTIONS}" + "${CG_ONEVALUEARGS}" + "${CG_MULTIVALUEARGS}" + "${ARGN}") + + # Variable to store the double-quote (") string. Since escaping + # double-quotes in strings is not possible we need a helper variable that + # does this job for us. + set(Q \") + + # Check invocation validity with the _UNPARSED_ARGUMENTS variable. + # If other not recognized parameters were passed, throw error. + if (CG_ARG_UNPARSED_ARGUMENTS) + set(CG_WARNMSG "Invocation of COMPILE_GRESOURCES with unrecognized") + set(CG_WARNMSG "${CG_WARNMSG} parameters. Parameters are:") + set(CG_WARNMSG "${CG_WARNMSG} ${CG_ARG_UNPARSED_ARGUMENTS}.") + message(WARNING ${CG_WARNMSG}) + endif() + + # Check invocation validity depending on generation mode (EMBED_C, EMBED_H + # or BUNDLE). + if ("${CG_ARG_TYPE}" STREQUAL "EMBED_C") + # EMBED_C mode, output compilable C-file. + list(APPEND CG_GENERATE_COMMAND_LINE --generate-source) + if (NOT "${CG_ARG_C_PREFIX}" STREQUAL "") + list(APPEND CG_GENERATE_COMMAND_LINE --c-name "${CG_ARG_C_PREFIX}") + endif() + set(CG_TARGET_FILE_ENDING "c") + elseif ("${CG_ARG_TYPE}" STREQUAL "EMBED_H") + # EMBED_H mode, output includable header file. + list(APPEND CG_GENERATE_COMMAND_LINE --generate-header) + if (NOT "${CG_ARG_C_PREFIX}" STREQUAL "") + list(APPEND CG_GENERATE_COMMAND_LINE --c-name "${CG_ARG_C_PREFIX}") + endif() + set(CG_TARGET_FILE_ENDING "h") + elseif ("${CG_ARG_TYPE}" STREQUAL "BUNDLE") + # BUNDLE mode, output resource bundle. Don't do anything since + # glib-compile-resources outputs a bundle when not specifying + # something else. + set(CG_TARGET_FILE_ENDING "gresource") + if (NOT "${CG_ARG_C_PREFIX}" STREQUAL "") + message(WARNING "Superfluously provided C_PREFIX=${CG_ARG_C_PREFIX} for BUNDLE.") + endif() + else() + # Everything else is AUTO mode, determine from target file ending. + if (CG_ARG_TARGET) + list(APPEND CG_GENERATE_COMMAND_LINE --generate) + else() + set(CG_ERRMSG "AUTO mode given, but no target specified. Can't") + set(CG_ERRMSG "${CG_ERRMSG} determine output type. In function") + set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + endif() + + # Check flag validity. + if (CG_ARG_COMPRESS_ALL AND CG_ARG_NO_COMPRESS_ALL) + set(CG_ERRMSG "COMPRESS_ALL and NO_COMPRESS_ALL simultaneously set. In") + set(CG_ERRMSG "${CG_ERRMSG} function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + if (CG_ARG_STRIPBLANKS_ALL AND CG_ARG_NO_STRIPBLANKS_ALL) + set(CG_ERRMSG "STRIPBLANKS_ALL and NO_STRIPBLANKS_ALL simultaneously") + set(CG_ERRMSG "${CG_ERRMSG} set. In function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + if (CG_ARG_TOPIXDATA_ALL AND CG_ARG_NO_TOPIXDATA_ALL) + set(CG_ERRMSG "TOPIXDATA_ALL and NO_TOPIXDATA_ALL simultaneously set.") + set(CG_ERRMSG "${CG_ERRMSG} In function COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + + # Check if there are any resources. + if (NOT CG_ARG_RESOURCES) + set(CG_ERRMSG "No resource files to process. In function") + set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.") + message(FATAL_ERROR ${CG_ERRMSG}) + endif() + + # If source directory is not set, default to working directory. + if (CG_ARG_SOURCE_DIR) + get_filename_component(CG_ARG_SOURCE_DIR "${CG_ARG_SOURCE_DIR}" + REALPATH BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") + else() + set(CG_ARG_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}") + endif() + + # Extract all dependencies for targets from resource list. + foreach(res ${CG_ARG_RESOURCES}) + if (NOT(("${res}" STREQUAL "COMPRESS") OR + ("${res}" STREQUAL "STRIPBLANKS") OR + ("${res}" STREQUAL "TOPIXDATA"))) + + list(APPEND CG_RESOURCES_DEPENDENCIES "${CG_ARG_SOURCE_DIR}/${res}") + endif() + endforeach() + + # Construct .gresource.xml path. + set(CG_XML_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources.gresource.xml") + + # Generate gresources XML target. + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_OUTPUT=${Q}${CG_XML_FILE_PATH}${Q}") + if(CG_ARG_COMPRESS_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_COMPRESS_ALL=ON") + endif() + if(CG_ARG_NO_COMPRESS_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_COMPRESS_ALL=ON") + endif() + if(CG_ARG_STRIPBLANKS_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_STRIPBLANKS_ALL=ON") + endif() + if(CG_ARG_NO_STRIPBLANKS_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_STRIPBLANKS_ALL=ON") + endif() + if(CG_ARG_TOPIXDATA_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_TOPIXDATA_ALL=ON") + endif() + if(CG_ARG_NO_TOPIXDATA_ALL) + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_TOPIXDATA_ALL=ON") + endif() + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_PREFIX=${Q}${CG_ARG_PREFIX}${Q}") + list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") + list(APPEND CG_CMAKE_SCRIPT_ARGS + "GXML_RESOURCES=${Q}${CG_ARG_RESOURCES}${Q}") + list(APPEND CG_CMAKE_SCRIPT_ARGS "-P") + list(APPEND CG_CMAKE_SCRIPT_ARGS + "${Q}${GCR_CMAKE_MACRO_DIR}/BuildTargetScript.cmake${Q}") + + get_filename_component(CG_XML_FILE_PATH_ONLY_NAME + "${CG_XML_FILE_PATH}" NAME) + set(CG_XML_CUSTOM_COMMAND_COMMENT + "Creating gresources XML file (${CG_XML_FILE_PATH_ONLY_NAME})") + add_custom_command(OUTPUT ${CG_XML_FILE_PATH} + COMMAND ${CMAKE_COMMAND} + ARGS ${CG_CMAKE_SCRIPT_ARGS} + DEPENDS ${CG_RESOURCES_DEPENDENCIES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT ${CG_XML_CUSTOM_COMMAND_COMMENT}) + + # Create target manually if not set (to make sure glib-compile-resources + # doesn't change behaviour with it's naming standards). + if (NOT CG_ARG_TARGET) + set(CG_ARG_TARGET "${CMAKE_CURRENT_BINARY_DIR}/resources.${CG_TARGET_FILE_ENDING}") + endif() + + # Add compilation target for resources. + add_custom_command(OUTPUT ${CG_ARG_TARGET} + COMMAND ${GLIB_COMPILE_RESOURCES_EXECUTABLE} + ARGS + ${CG_ARG_OPTIONS} + --target ${CG_ARG_TARGET} + --sourcedir ${CG_ARG_SOURCE_DIR} + ${CG_GENERATE_COMMAND_LINE} + ${CG_XML_FILE_PATH} + VERBATIM + MAIN_DEPENDENCY ${CG_XML_FILE_PATH} + DEPENDS ${CG_RESOURCES_DEPENDENCIES} + WORKING_DIRECTORY ${CMAKE_BUILD_DIR}) + + # Set output and XML_OUT to parent scope. + set(${xml_out} ${CG_XML_FILE_PATH} PARENT_SCOPE) + set(${output} ${CG_ARG_TARGET} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Modules/GenerateGXML.cmake b/cmake/Modules/GenerateGXML.cmake new file mode 100644 index 0000000..b3f1a30 --- /dev/null +++ b/cmake/Modules/GenerateGXML.cmake @@ -0,0 +1,124 @@ +include(CMakeParseArguments) + +# Generates the resource XML controlling file from resource list (and saves it +# to xml_path). It's not recommended to use this function directly, since it +# doesn't handle invalid arguments. It is used by the function +# COMPILE_GRESOURCES() to create a custom command, so that this function is +# invoked at build-time in script mode from CMake. +function(GENERATE_GXML xml_path) + # Available options: + # COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all + # registered resources. + # STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all + # registered resources. + # TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all + # registered resources. + set(GXML_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL + STRIPBLANKS_ALL NO_STRIPBLANKS_ALL + TOPIXDATA_ALL NO_TOPIXDATA_ALL) + + # Available one value options: + # PREFIX Overrides the resource prefix that is prepended to each + # relative file name in registered resources. + set(GXML_ONEVALUEARGS PREFIX) + + # Available multi-value options: + # RESOURCES The list of resource files. Whether absolute or relative path is + # equal, absolute paths are stripped down to relative ones. If the + # absolute path is not inside the given base directory SOURCE_DIR + # or CMAKE_CURRENT_SOURCE_DIR (if SOURCE_DIR is not overriden), + # this function aborts. + set(GXML_MULTIVALUEARGS RESOURCES) + + # Parse the arguments. + cmake_parse_arguments(GXML_ARG + "${GXML_OPTIONS}" + "${GXML_ONEVALUEARGS}" + "${GXML_MULTIVALUEARGS}" + "${ARGN}") + + # Variable to store the double-quote (") string. Since escaping + # double-quotes in strings is not possible we need a helper variable that + # does this job for us. + set(Q \") + + # Process resources and generate XML file. + # Begin with the XML header and header nodes. + set(GXML_XML_FILE "") + set(GXML_XML_FILE "${GXML_XML_FILE}") + + # Process each resource. + foreach(res ${GXML_ARG_RESOURCES}) + if ("${res}" STREQUAL "COMPRESS") + set(GXML_COMPRESSION_FLAG ON) + elseif ("${res}" STREQUAL "STRIPBLANKS") + set(GXML_STRIPBLANKS_FLAG ON) + elseif ("${res}" STREQUAL "TOPIXDATA") + set(GXML_TOPIXDATA_FLAG ON) + else() + # The file name. + set(GXML_RESOURCE_PATH "${res}") + + # Append to real resource file dependency list. + list(APPEND GXML_RESOURCES_DEPENDENCIES ${GXML_RESOURCE_PATH}) + + # Assemble node. + set(GXML_RES_LINE "${GXML_RESOURCE_PATH}") + + # Append to file string. + set(GXML_XML_FILE "${GXML_XML_FILE}${GXML_RES_LINE}") + + # Unset variables. + unset(GXML_COMPRESSION_FLAG) + unset(GXML_STRIPBLANKS_FLAG) + unset(GXML_TOPIXDATA_FLAG) + endif() + + endforeach() + + # Append closing nodes. + set(GXML_XML_FILE "${GXML_XML_FILE}") + + # Use "file" function to generate XML controlling file. + get_filename_component(xml_path_only_name "${xml_path}" NAME) + file(WRITE ${xml_path} ${GXML_XML_FILE}) + +endfunction() + diff --git a/cmake/Modules/GlibCompileResourcesSupport.cmake b/cmake/Modules/GlibCompileResourcesSupport.cmake new file mode 100644 index 0000000..2950af3 --- /dev/null +++ b/cmake/Modules/GlibCompileResourcesSupport.cmake @@ -0,0 +1,11 @@ +# Path to this file. +set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# Finds the glib-compile-resources executable. +find_program(GLIB_COMPILE_RESOURCES_EXECUTABLE glib-compile-resources) +mark_as_advanced(GLIB_COMPILE_RESOURCES_EXECUTABLE) + +# Include the cmake files containing the functions. +include(${GCR_CMAKE_MACRO_DIR}/CompileGResources.cmake) +include(${GCR_CMAKE_MACRO_DIR}/GenerateGXML.cmake) + diff --git a/coro/GLibMainContextExecutor.cpp b/coro/GLibMainContextExecutor.cpp index 226af3a..a49605f 100644 --- a/coro/GLibMainContextExecutor.cpp +++ b/coro/GLibMainContextExecutor.cpp @@ -3,7 +3,7 @@ // #include "GLibMainContextExecutor.h" -#include +#include int callback(void* data) { auto executor = reinterpret_cast(data); diff --git a/gtkpp/Application.cpp b/gtkpp/Application.cpp index 76ec3b5..a827e61 100644 --- a/gtkpp/Application.cpp +++ b/gtkpp/Application.cpp @@ -30,8 +30,15 @@ namespace gtkpp { } void Application::addWindow(const std::shared_ptr& window) { - _window = window; + _windows.push_back(window); gtk_application_add_window(GTK_APPLICATION(_app), GTK_WINDOW(window->gobj())); } + void Application::removeWindow(Window* window) { + std::remove_if(_windows.begin(), + _windows.end(), + [window](const auto& ptr) { return ptr.get() == window; }); + gtk_application_remove_window(GTK_APPLICATION(_app), GTK_WINDOW(window->gobj())); + } + } diff --git a/gtkpp/Application.h b/gtkpp/Application.h index 69eae34..ea289da 100644 --- a/gtkpp/Application.h +++ b/gtkpp/Application.h @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace gtkpp { @@ -17,7 +18,7 @@ namespace gtkpp { class Application { private: AdwApplication* _app; - std::shared_ptr _window; + std::vector> _windows; sigc::signal _signalActivate; private: @@ -29,6 +30,7 @@ namespace gtkpp { void onActivate(const std::function& callback); AdwApplication* gobj() const; void addWindow(const std::shared_ptr& window); + void removeWindow(Window* window); }; } diff --git a/gtkpp/Leaflet.cpp b/gtkpp/Leaflet.cpp new file mode 100644 index 0000000..9a871bc --- /dev/null +++ b/gtkpp/Leaflet.cpp @@ -0,0 +1,18 @@ +// +// Created by selim on 15.05.2022. +// + +#include "Leaflet.h" +#include + +namespace gtkpp { + + Leaflet::Leaflet(): Widget() { + _widget = adw_leaflet_new(); + } + + void Leaflet::append(Widget *widget) { + adw_leaflet_append(ADW_LEAFLET(_widget), widget->gobj()); + } + +} diff --git a/gtkpp/Leaflet.h b/gtkpp/Leaflet.h new file mode 100644 index 0000000..380a8d5 --- /dev/null +++ b/gtkpp/Leaflet.h @@ -0,0 +1,20 @@ +// +// Created by selim on 15.05.2022. +// + +#ifndef AUTOCAT_GNOME_LEAFLET_H +#define AUTOCAT_GNOME_LEAFLET_H + +#include "Widget.h" + +namespace gtkpp { + + class Leaflet: public Widget { + public: + Leaflet(); + void append(Widget* widget); + }; + +} + +#endif //AUTOCAT_GNOME_LEAFLET_H diff --git a/gtkpp/MessageDialog.cpp b/gtkpp/MessageDialog.cpp new file mode 100644 index 0000000..7c5cf27 --- /dev/null +++ b/gtkpp/MessageDialog.cpp @@ -0,0 +1,21 @@ +// +// Created by selim on 14.05.2022. +// + +#include "MessageDialog.h" + +void gtkpp::MessageDialog::showError(gtkpp::Window *parent, const std::string &text) { + auto flags = static_cast(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL); + auto dialog = gtk_message_dialog_new(GTK_WINDOW(parent->gobj()), + flags, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + text.c_str()); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_window_destroy), + nullptr); + + gtk_window_present(GTK_WINDOW(dialog)); +} diff --git a/gtkpp/MessageDialog.h b/gtkpp/MessageDialog.h new file mode 100644 index 0000000..1455f36 --- /dev/null +++ b/gtkpp/MessageDialog.h @@ -0,0 +1,19 @@ +// +// Created by selim on 14.05.2022. +// + +#ifndef AUTOCAT_GNOME_MESSAGEDIALOG_H +#define AUTOCAT_GNOME_MESSAGEDIALOG_H + +#include "Window.h" + +namespace gtkpp { + + class MessageDialog { + public: + static void showError(Window* parent, const std::string& text); + }; + +} + +#endif //AUTOCAT_GNOME_MESSAGEDIALOG_H diff --git a/gtkpp/Window.cpp b/gtkpp/Window.cpp index 02b7668..dfe5b7e 100644 --- a/gtkpp/Window.cpp +++ b/gtkpp/Window.cpp @@ -4,26 +4,59 @@ #include "Window.h" -gtkpp::Window::Window() { - _window = ADW_WINDOW(adw_window_new()); -} +namespace gtkpp { -gtkpp::Window::Window(std::shared_ptr app) { - _window = ADW_WINDOW(adw_application_window_new(GTK_APPLICATION(app->gobj()))); -} + bool operator==(const Window& wnd1, const Window& wnd2) { + return wnd1._window == wnd2._window; + } -void gtkpp::Window::show() { - gtk_window_present(GTK_WINDOW(_window)); -} + Window::Window(std::shared_ptr app, bool isAppWindow) { + if(isAppWindow) { + _window = GTK_WINDOW(adw_application_window_new(GTK_APPLICATION(app->gobj()))); + } else { + _window = GTK_WINDOW(adw_window_new()); + } -void gtkpp::Window::setTitle(const std::string &title) { - gtk_window_set_title(GTK_WINDOW(_window), title.c_str()); -} + _app = app; + _builder = nullptr; + } -AdwWindow *gtkpp::Window::gobj() const { - return _window; -} + Window::Window(std::shared_ptr app, const char* resourceName) { + _app = app; +// _builder = gtk_builder_new(); +// +// GError* error = nullptr; +// gtk_builder_add_from_resource(_builder, resourceName, &error); +// if(error) { +// throw std::runtime_error(error->message); +// } + + _builder = gtk_builder_new_from_resource(resourceName); + _window = GTK_WINDOW(gtk_builder_get_object(_builder, "adw_main_window")); + } + + void Window::show() { + gtk_window_present(GTK_WINDOW(_window)); + } + + void Window::setTitle(const std::string &title) { + gtk_window_set_title(GTK_WINDOW(_window), title.c_str()); + } + + GtkWindow *Window::gobj() const { + return _window; + } + + void Window::setDefaultSize(int width, int height) { + gtk_window_set_default_size(GTK_WINDOW(_window), width, height); + } + + std::shared_ptr Window::application() const { + return _app.lock(); + } + + void Window::hide() { + gtk_window_close(GTK_WINDOW(_window)); + } -void gtkpp::Window::setDefaultSize(int width, int height) { - gtk_window_set_default_size(GTK_WINDOW(_window), width, height); } diff --git a/gtkpp/Window.h b/gtkpp/Window.h index d2a38b9..ec16039 100644 --- a/gtkpp/Window.h +++ b/gtkpp/Window.h @@ -13,15 +13,22 @@ namespace gtkpp { class Window { protected: - AdwWindow* _window; + GtkBuilder* _builder; + GtkWindow* _window; + std::weak_ptr _app; + + private: + friend bool operator==(const Window& wnd1, const Window& wnd2); public: - Window(); - explicit Window(std::shared_ptr app); + Window(std::shared_ptr app, bool isAppWindow); + Window(std::shared_ptr app, const char* resourceName); void show(); + void hide(); void setTitle(const std::string& title); void setDefaultSize(int width, int height); - AdwWindow* gobj() const; + [[nodiscard]] GtkWindow* gobj() const; + [[nodiscard]] std::shared_ptr application() const; }; } diff --git a/gui/LoginWindow.cpp b/gui/LoginWindow.cpp index eaa634d..14baa11 100644 --- a/gui/LoginWindow.cpp +++ b/gui/LoginWindow.cpp @@ -8,12 +8,14 @@ #include "../coro/GLibMainContextExecutor.h" #include "../gtkpp/HeaderBar.h" +#include "../gtkpp/MessageDialog.h" #include +#include #include #include -LoginWindow::LoginWindow() { +LoginWindow::LoginWindow(std::shared_ptr app): gtkpp::Window(std::move(app), false) { setDefaultSize(640, 480); @@ -54,19 +56,20 @@ void LoginWindow::loginClicked() { enableControls(false); _spinner.start(); -// try { -// User user = co_await Api::login(email, password).scheduleOn(GLibMainContextExecutor::instance()); -// auto app = this->get_application(); -// auto mainWindow = new MainWindow(); -// mainWindow->show(); -// hide(); -// app->add_window(*mainWindow); -// app->remove_window(*this); -// } catch (std::exception& ex) { -// enableControls(true); -// _spinner.stop(); -// showError(ex.what()); -// } + try { + User user = co_await Api::login(email, password).scheduleOn(GLibMainContextExecutor::instance()); + if(auto app = this->application()) { + auto mainWindow = std::make_shared(app); + mainWindow->show(); + app->addWindow(mainWindow); + app->removeWindow(this); + hide(); + } + } catch (std::exception& ex) { + enableControls(true); + _spinner.stop(); + gtkpp::MessageDialog::showError(this, ex.what()); + } } void LoginWindow::validateFields() { @@ -74,19 +77,6 @@ void LoginWindow::validateFields() { _loginButton.setEnabled(buttonEnabled); } -void LoginWindow::showError(const std::string& message) { -// _dialog = std::make_unique("Error", -// false, -// Gtk::MessageType::ERROR, -// Gtk::ButtonsType::OK, -// true); -// _dialog->set_secondary_text(message); -// _dialog->set_transient_for(*this); -// _dialog->set_hide_on_close(true); -// _dialog->signal_response().connect(sigc::hide(sigc::mem_fun(*_dialog, &Gtk::Widget::hide))); -// _dialog->show(); -} - void LoginWindow::enableControls(bool enable) { _loginButton.setEnabled(enable); _loginEntry.setEnabled(enable); diff --git a/gui/LoginWindow.h b/gui/LoginWindow.h index afa46d4..823494b 100644 --- a/gui/LoginWindow.h +++ b/gui/LoginWindow.h @@ -11,26 +11,20 @@ #include "../gtkpp/Entry.h" #include "../gtkpp/Button.h" #include "../gtkpp/Spinner.h" +#include "../gtkpp/Application.h" class LoginWindow: public gtkpp::Window { private: -// Gtk::Entry _emailField; -// Gtk::Entry _passwordField; -// Gtk::Button _loginButton; -// Gtk::Spinner _spinner; -// std::unique_ptr _dialog; - gtkpp::Entry _loginEntry; gtkpp::Entry _passwordEntry; gtkpp::Button _loginButton; gtkpp::Spinner _spinner; public: - LoginWindow(); + explicit LoginWindow(std::shared_ptr app); void loginClicked(); void validateFields(); - void showError(const std::string& message); void enableControls(bool enable); }; diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 1f17714..af6887a 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -3,34 +3,15 @@ // #include "MainWindow.h" -#include +#include "../gtkpp/Box.h" +#include "../gtkpp/HeaderBar.h" -MainWindow::MainWindow() { - //set_title("Main window"); - set_default_size(640, 480); - //set_decorated(false); +MainWindow::MainWindow(std::shared_ptr app): gtkpp::Window(std::move(app), "/gui/MainWindow.xml") { - set_titlebar(_titleBar); - - _masterLabel.set_text("master"); - _masterBox.set_orientation(Gtk::Orientation::VERTICAL); - _masterBox.append(_masterLabel); - - _detailLabel.set_text("detail"); - _detailBox.set_orientation(Gtk::Orientation::VERTICAL); - _detailBox.append(_detailLabel); - - _paned.set_start_child(_masterBox); - _paned.set_end_child(_detailBox); - _paned.set_resize_start_child(true); - _paned.set_shrink_end_child(true); - _paned.set_resize_end_child(true); - set_child(_paned); - - _sizeGroup = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL); - _sizeGroup->add_widget(_titleBar.masterHeader()); - _sizeGroup->add_widget(_masterBox); - - _titleBar.masterHeader().set_size_request(50, -1); - _masterBox.set_size_request(50, -1); +// gtkpp::Box rootBox(GTK_ORIENTATION_VERTICAL, 0); +// rootBox.append(gtkpp::HeaderBar("Main")); +// +// _leaflet.append(&rootBox); +// +// adw_application_window_set_content(ADW_APPLICATION_WINDOW(_window), _leaflet.gobj()); } diff --git a/gui/MainWindow.h b/gui/MainWindow.h index c450b71..aa87b5b 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -5,26 +5,15 @@ #ifndef AUTOCAT_GNOME_MAINWINDOW_H #define AUTOCAT_GNOME_MAINWINDOW_H -#include "TitleBar.h" +#include "../gtkpp/Window.h" +#include "../gtkpp/Leaflet.h" -#include -#include -#include -#include -#include - -class MainWindow: public Gtk::Window { +class MainWindow: public gtkpp::Window { private: - TitleBar _titleBar; - Gtk::Paned _paned; - Gtk::Label _masterLabel; - Gtk::Label _detailLabel; - Gtk::Box _masterBox; - Gtk::Box _detailBox; - Glib::RefPtr _sizeGroup; + gtkpp::Leaflet _leaflet; public: - MainWindow(); + explicit MainWindow(std::shared_ptr app); }; diff --git a/gui/MainWindow.xml b/gui/MainWindow.xml new file mode 100644 index 0000000..b3a5401 --- /dev/null +++ b/gui/MainWindow.xml @@ -0,0 +1,54 @@ + + + + + 640 + 480 + true + + + + true + + + 240 + vertical + + + + + + adw_leaflet + + + + + Application + + + + + + none + + + + + + + + + false + + + + + + + + + \ No newline at end of file diff --git a/gui/TitleBar.cpp b/gui/TitleBar.cpp deleted file mode 100644 index 334e47f..0000000 --- a/gui/TitleBar.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by selim on 13.02.2022. -// - -#include "TitleBar.h" - -TitleBar::TitleBar() { - set_start_child(_masterHeader); - set_end_child(_detailHeader); - set_resize_start_child(true); - set_shrink_end_child(true); - set_resize_end_child(true); -} - -Gtk::HeaderBar &TitleBar::masterHeader() { - return _masterHeader; -} - -Gtk::HeaderBar &TitleBar::detailHeader() { - return _detailHeader; -} diff --git a/gui/TitleBar.h b/gui/TitleBar.h deleted file mode 100644 index ce67fd5..0000000 --- a/gui/TitleBar.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by selim on 13.02.2022. -// - -#ifndef AUTOCAT_GNOME_TITLEBAR_H -#define AUTOCAT_GNOME_TITLEBAR_H - -#include -#include - -class TitleBar: public Gtk::Paned { -private: - Gtk::HeaderBar _masterHeader; - Gtk::HeaderBar _detailHeader; - -public: - TitleBar(); - Gtk::HeaderBar& masterHeader(); - Gtk::HeaderBar& detailHeader(); -}; - - -#endif //AUTOCAT_GNOME_TITLEBAR_H diff --git a/main.cpp b/main.cpp index 4bd8e95..f0c245f 100644 --- a/main.cpp +++ b/main.cpp @@ -4,50 +4,30 @@ #include "gtkpp/Application.h" #include "gtkpp/Window.h" -#include -#include -#include #include #include #include -//std::unique_ptr createStartWindow() { -// auto settings = Settings::instance(); -// if(settings.user().token.empty()) { -// return std::make_unique(); -// } else { -// return std::make_unique(); -// } -//} +std::shared_ptr createMainWindow(std::shared_ptr app) { + auto settings = Settings::instance(); + if(settings.user().token.empty()) { + return std::make_shared(app); + } else { + return std::make_shared(app); + } +} int main(int argc, char* argv[]) { folly::init(&argc, &argv); - -// auto app = Gtk::Application::create("pro.aliencat.aliencat"); -// auto window = createStartWindow(); -// -// app->signal_activate().connect([&](){ -// app->add_window(*window); -// window->show(); -// }); -// return app->run(argc, argv); + g_resources_register(g_resource_load("resources.gresource", nullptr)); auto app = std::make_shared("pro.aliencat.autocat"); app->onActivate([&](){ - auto settings = Settings::instance(); - - if(settings.user().token.empty()) { - auto window = std::make_shared(); - app->addWindow(window); - window->setTitle("Login"); - window->show(); - } else { - auto window = std::make_unique(app); - window->setTitle("Some title"); - window->show(); - } + auto window = createMainWindow(app); + app->addWindow(window); + window->show(); }); return app->run(argc, argv);