Project migrated from gtkmm to plain gtk. Main window now loading from xml (located in resources)

This commit is contained in:
Selim Mustafaev 2022-05-15 18:42:33 +03:00
parent 458901c029
commit 28b18aa67b
22 changed files with 704 additions and 190 deletions

View File

@ -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})

View File

@ -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})

View File

@ -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 <prefix>_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()

View File

@ -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 "<?xml version=${Q}1.0${Q} encoding=${Q}UTF-8${Q}?>")
set(GXML_XML_FILE "${GXML_XML_FILE}<gresources><gresource prefix=${Q}")
# Set the prefix for the resources. Depending on the user-override we choose
# the standard prefix "/" or the override.
if (GXML_ARG_PREFIX)
set(GXML_XML_FILE "${GXML_XML_FILE}${GXML_ARG_PREFIX}")
else()
set(GXML_XML_FILE "${GXML_XML_FILE}/")
endif()
set(GXML_XML_FILE "${GXML_XML_FILE}${Q}>")
# 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 <file> node.
set(GXML_RES_LINE "<file")
if ((GXML_ARG_COMPRESS_ALL OR GXML_COMPRESSION_FLAG) AND NOT
GXML_ARG_NO_COMPRESS_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} compressed=${Q}true${Q}")
endif()
# Check preprocess flag validity.
if ((GXML_ARG_STRIPBLANKS_ALL OR GXML_STRIPBLANKS_FLAG) AND
(GXML_ARG_TOPIXDATA_ALL OR GXML_TOPIXDATA_FLAG))
set(GXML_ERRMSG "Resource preprocessing option conflict. Tried")
set(GXML_ERRMSG "${GXML_ERRMSG} to specify both, STRIPBLANKS")
set(GXML_ERRMSG "${GXML_ERRMSG} and TOPIXDATA. In resource")
set(GXML_ERRMSG "${GXML_ERRMSG} ${GXML_RESOURCE_PATH} in")
set(GXML_ERRMSG "${GXML_ERRMSG} function COMPILE_GRESOURCES.")
message(FATAL_ERROR ${GXML_ERRMSG})
endif()
if ((GXML_ARG_STRIPBLANKS_ALL OR GXML_STRIPBLANKS_FLAG) AND NOT
GXML_ARG_NO_STRIPBLANKS_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} preprocess=")
set(GXML_RES_LINE "${GXML_RES_LINE}${Q}xml-stripblanks${Q}")
elseif((GXML_ARG_TOPIXDATA_ALL OR GXML_TOPIXDATA_FLAG) AND NOT
GXML_ARG_NO_TOPIXDATA_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} preprocess=")
set(GXML_RES_LINE "${GXML_RES_LINE}${Q}to-pixdata${Q}")
endif()
set(GXML_RES_LINE "${GXML_RES_LINE}>${GXML_RESOURCE_PATH}</file>")
# 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}</gresource></gresources>")
# 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()

View File

@ -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)

View File

@ -3,7 +3,7 @@
//
#include "GLibMainContextExecutor.h"
#include <glibmm.h>
#include <glib.h>
int callback(void* data) {
auto executor = reinterpret_cast<GLibMainContextExecutor*>(data);

View File

@ -30,8 +30,15 @@ namespace gtkpp {
}
void Application::addWindow(const std::shared_ptr<Window>& 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()));
}
}

View File

@ -8,6 +8,7 @@
#include <adwaita.h>
#include <string>
#include <functional>
#include <vector>
#include <sigc++/sigc++.h>
namespace gtkpp {
@ -17,7 +18,7 @@ namespace gtkpp {
class Application {
private:
AdwApplication* _app;
std::shared_ptr<Window> _window;
std::vector<std::shared_ptr<Window>> _windows;
sigc::signal<void()> _signalActivate;
private:
@ -29,6 +30,7 @@ namespace gtkpp {
void onActivate(const std::function<void()>& callback);
AdwApplication* gobj() const;
void addWindow(const std::shared_ptr<Window>& window);
void removeWindow(Window* window);
};
}

18
gtkpp/Leaflet.cpp Normal file
View File

@ -0,0 +1,18 @@
//
// Created by selim on 15.05.2022.
//
#include "Leaflet.h"
#include <adwaita.h>
namespace gtkpp {
Leaflet::Leaflet(): Widget() {
_widget = adw_leaflet_new();
}
void Leaflet::append(Widget *widget) {
adw_leaflet_append(ADW_LEAFLET(_widget), widget->gobj());
}
}

20
gtkpp/Leaflet.h Normal file
View File

@ -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

21
gtkpp/MessageDialog.cpp Normal file
View File

@ -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<GtkDialogFlags>(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));
}

19
gtkpp/MessageDialog.h Normal file
View File

@ -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

View File

@ -4,26 +4,59 @@
#include "Window.h"
gtkpp::Window::Window() {
_window = ADW_WINDOW(adw_window_new());
namespace gtkpp {
bool operator==(const Window& wnd1, const Window& wnd2) {
return wnd1._window == wnd2._window;
}
gtkpp::Window::Window(std::shared_ptr<gtkpp::Application> app) {
_window = ADW_WINDOW(adw_application_window_new(GTK_APPLICATION(app->gobj())));
Window::Window(std::shared_ptr<Application> 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::show() {
_app = app;
_builder = nullptr;
}
Window::Window(std::shared_ptr<Application> 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 gtkpp::Window::setTitle(const std::string &title) {
void Window::setTitle(const std::string &title) {
gtk_window_set_title(GTK_WINDOW(_window), title.c_str());
}
AdwWindow *gtkpp::Window::gobj() const {
GtkWindow *Window::gobj() const {
return _window;
}
void gtkpp::Window::setDefaultSize(int width, int height) {
void Window::setDefaultSize(int width, int height) {
gtk_window_set_default_size(GTK_WINDOW(_window), width, height);
}
std::shared_ptr<Application> Window::application() const {
return _app.lock();
}
void Window::hide() {
gtk_window_close(GTK_WINDOW(_window));
}
}

View File

@ -13,15 +13,22 @@ namespace gtkpp {
class Window {
protected:
AdwWindow* _window;
GtkBuilder* _builder;
GtkWindow* _window;
std::weak_ptr<Application> _app;
private:
friend bool operator==(const Window& wnd1, const Window& wnd2);
public:
Window();
explicit Window(std::shared_ptr<Application> app);
Window(std::shared_ptr<Application> app, bool isAppWindow);
Window(std::shared_ptr<Application> 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> application() const;
};
}

View File

@ -8,12 +8,14 @@
#include "../coro/GLibMainContextExecutor.h"
#include "../gtkpp/HeaderBar.h"
#include "../gtkpp/MessageDialog.h"
#include <iostream>
#include <utility>
#include <folly/experimental/coro/Task.h>
#include <folly/executors/IOThreadPoolExecutor.h>
LoginWindow::LoginWindow() {
LoginWindow::LoginWindow(std::shared_ptr<gtkpp::Application> 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<MainWindow>(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<Gtk::MessageDialog>("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);

View File

@ -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<Gtk::MessageDialog> _dialog;
gtkpp::Entry _loginEntry;
gtkpp::Entry _passwordEntry;
gtkpp::Button _loginButton;
gtkpp::Spinner _spinner;
public:
LoginWindow();
explicit LoginWindow(std::shared_ptr<gtkpp::Application> app);
void loginClicked();
void validateFields();
void showError(const std::string& message);
void enableControls(bool enable);
};

View File

@ -3,34 +3,15 @@
//
#include "MainWindow.h"
#include <gdkmm.h>
#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<gtkpp::Application> 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());
}

View File

@ -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 <gtkmm/window.h>
#include <gtkmm/paned.h>
#include <gtkmm/label.h>
#include <gtkmm/box.h>
#include <gtkmm/sizegroup.h>
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<Gtk::SizeGroup> _sizeGroup;
gtkpp::Leaflet _leaflet;
public:
MainWindow();
explicit MainWindow(std::shared_ptr<gtkpp::Application> app);
};

54
gui/MainWindow.xml Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="AdwApplicationWindow" id="adw_main_window">
<property name="default-width">640</property>
<property name="default-height">480</property>
<property name="resizable">true</property>
<child>
<object class="AdwLeaflet" id="adw_leaflet">
<property name="vexpand">true</property>
<child>
<object class="GtkBox">
<property name="width-request">240</property>
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar" id="adw_headerBar">
<!--
<style>
<class name="flat"></class>
</style>
-->
<binding name="show-end-title-buttons">
<lookup name="folded">adw_leaflet</lookup>
</binding>
<property name="title-widget">
<object class="AdwWindowTitle">
<property name="title" translatable="yes">Application</property>
</object>
</property>
<child type="end">
<object class="GtkMenuButton" id="gtk_btnHeaderHelp">
<property name="direction">none</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwLeafletPage">
<property name="navigatable">false</property>
<property name="child">
<object class="GtkSeparator"/>
</property>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@ -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;
}

View File

@ -1,23 +0,0 @@
//
// Created by selim on 13.02.2022.
//
#ifndef AUTOCAT_GNOME_TITLEBAR_H
#define AUTOCAT_GNOME_TITLEBAR_H
#include <gtkmm/paned.h>
#include <gtkmm/headerbar.h>
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

View File

@ -4,50 +4,30 @@
#include "gtkpp/Application.h"
#include "gtkpp/Window.h"
#include <gtkmm/application.h>
#include <glibmm.h>
#include <gtkmm.h>
#include <memory>
#include <folly/init/Init.h>
#include <iostream>
//std::unique_ptr<Gtk::Window> createStartWindow() {
// auto settings = Settings::instance();
// if(settings.user().token.empty()) {
// return std::make_unique<LoginWindow>();
// } else {
// return std::make_unique<MainWindow>();
// }
//}
std::shared_ptr<gtkpp::Window> createMainWindow(std::shared_ptr<gtkpp::Application> app) {
auto settings = Settings::instance();
if(settings.user().token.empty()) {
return std::make_shared<LoginWindow>(app);
} else {
return std::make_shared<MainWindow>(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<gtkpp::Application>("pro.aliencat.autocat");
app->onActivate([&](){
auto settings = Settings::instance();
if(settings.user().token.empty()) {
auto window = std::make_shared<LoginWindow>();
auto window = createMainWindow(app);
app->addWindow(window);
window->setTitle("Login");
window->show();
} else {
auto window = std::make_unique<gtkpp::Window>(app);
window->setTitle("Some title");
window->show();
}
});
return app->run(argc, argv);