latest changes
This commit is contained in:
parent
8caa12b4ce
commit
46a8b84869
@ -2,19 +2,35 @@ cmake_minimum_required(VERSION 3.0)
|
|||||||
project(autocat_gnome)
|
project(autocat_gnome)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g -Og")
|
||||||
|
|
||||||
#if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
find_package(PkgConfig REQUIRED)
|
||||||
# add_compile_options(-stdlib=libc++)
|
find_package(nlohmann_json REQUIRED)
|
||||||
#endif()
|
find_package(Boost 1.70 COMPONENTS thread REQUIRED)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
|
||||||
|
|
||||||
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
|
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
|
||||||
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
|
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
|
||||||
pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4)
|
pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4)
|
||||||
|
pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1)
|
||||||
|
|
||||||
include_directories(${GTKMM_INCLUDE_DIRS} ${GLIBMM_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS})
|
include_directories(${GTKMM_INCLUDE_DIRS}
|
||||||
link_directories(${GTKMM_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} ${LIBSOUP_LIBRARY_DIRS})
|
${GLIBMM_INCLUDE_DIRS}
|
||||||
|
${LIBSOUP_INCLUDE_DIRS}
|
||||||
|
${Boost_INCLUDE_DIR}
|
||||||
|
${LIBADWAITA_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h coro/Task.h models/User.cpp models/User.h)
|
link_directories(${GTKMM_LIBRARY_DIRS}
|
||||||
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES} ${GLIBMM_LIBRARIES} ${LIBSOUP_LIBRARIES})
|
${GLIBMM_LIBRARY_DIRS}
|
||||||
|
${LIBSOUP_LIBRARY_DIRS}
|
||||||
|
${Boost_LIBRARY_DIRS}
|
||||||
|
${LIBADWAITA_LIBRARY_DIRS})
|
||||||
|
|
||||||
|
add_definitions(-DBOOST_THREAD_PROVIDES_FUTURE -DBOOST_THREAD_PROVIDES_FUTURE_CONTINUATION -DBOOST_THREAD_PROVIDES_PROMISE_LAZY -DBOOST_THREAD_VERSION=4)
|
||||||
|
|
||||||
|
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h coro/Task.h models/User.cpp models/User.h services/Settings.cpp services/Settings.h app/App.cpp app/App.h gui/TitleBar.cpp gui/TitleBar.h)
|
||||||
|
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES}
|
||||||
|
${GLIBMM_LIBRARIES}
|
||||||
|
${LIBSOUP_LIBRARIES}
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
${LIBADWAITA_LIBRARIES})
|
||||||
|
|||||||
48
app/App.cpp
Normal file
48
app/App.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 02.02.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "App.h"
|
||||||
|
#include "../gui/MainWindow.h"
|
||||||
|
|
||||||
|
static void activateApp(GtkApplication *gtkApp, void* data) {
|
||||||
|
auto app = reinterpret_cast<App*>(data);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto win = std::make_shared<MainWindow>();
|
||||||
|
app->setWindow(win);
|
||||||
|
|
||||||
|
// GtkWidget *window = gtk_application_window_new (gtkApp);
|
||||||
|
// GtkWidget *label = gtk_label_new ("Hello World");
|
||||||
|
//
|
||||||
|
// gtk_window_set_title (GTK_WINDOW (window), "Hello");
|
||||||
|
// gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
|
||||||
|
// gtk_window_set_child (GTK_WINDOW (window), label);
|
||||||
|
// gtk_window_present (GTK_WINDOW (window));
|
||||||
|
}
|
||||||
|
|
||||||
|
App::App(): _app(nullptr), _window(nullptr) {
|
||||||
|
_app = adw_application_new("pro.aliencat.aliencat", G_APPLICATION_FLAGS_NONE);
|
||||||
|
g_signal_connect_data(_app, "activate", G_CALLBACK(activateApp), this, nullptr, G_CONNECT_AFTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
App::~App() {
|
||||||
|
glib_autoptr_cleanup_AdwApplication(&_app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int App::run(int argc, char **argv) {
|
||||||
|
return g_application_run(G_APPLICATION(_app), argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::setWindow(std::shared_ptr<Gtk::Window> window) {
|
||||||
|
gtk_application_add_window(GTK_APPLICATION(_app), window->gobj());
|
||||||
|
|
||||||
|
if(_window) {
|
||||||
|
_window->hide();
|
||||||
|
gtk_application_remove_window(GTK_APPLICATION(_app), _window->gobj());
|
||||||
|
}
|
||||||
|
|
||||||
|
window->show();
|
||||||
|
_window = window;
|
||||||
|
}
|
||||||
25
app/App.h
Normal file
25
app/App.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 02.02.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef AUTOCAT_GNOME_APP_H
|
||||||
|
#define AUTOCAT_GNOME_APP_H
|
||||||
|
|
||||||
|
#include <gtkmm/window.h>
|
||||||
|
#include <adwaita.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class App {
|
||||||
|
private:
|
||||||
|
AdwApplication* _app;
|
||||||
|
std::shared_ptr<Gtk::Window> _window;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit App();
|
||||||
|
~App();
|
||||||
|
int run(int argc, char** argv);
|
||||||
|
void setWindow(std::shared_ptr<Gtk::Window> window);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //AUTOCAT_GNOME_APP_H
|
||||||
198
coro/Task.h
198
coro/Task.h
@ -8,94 +8,140 @@
|
|||||||
#include <coroutine>
|
#include <coroutine>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <boost/thread/future.hpp>
|
||||||
|
#include <boost/exception/errinfo_nested_exception.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include <glibmm.h>
|
||||||
|
|
||||||
|
class FutureException: public virtual std::exception, public virtual boost::exception {
|
||||||
|
private:
|
||||||
|
std::string _message;
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class Task {
|
|
||||||
public:
|
public:
|
||||||
|
explicit FutureException(const char* msg): _message(msg) {}
|
||||||
|
explicit FutureException(std::string msg): _message(std::move(msg)) {}
|
||||||
|
|
||||||
|
const char* what() const noexcept {
|
||||||
|
return _message.c_str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enable the use of std::future<T> as a coroutine type
|
||||||
|
// by using a std::promise<T> as the promise type.
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
requires(!std::is_void_v<T> && !std::is_reference_v<T>)
|
||||||
|
struct std::coroutine_traits<boost::future<T>, Args...> {
|
||||||
|
struct promise_type : boost::promise<T> {
|
||||||
|
boost::future<T> get_return_object() noexcept {
|
||||||
|
return this->get_future();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::suspend_never initial_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::suspend_never final_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_value(const T &value)
|
||||||
|
noexcept(std::is_nothrow_copy_constructible_v<T>) {
|
||||||
|
this->set_value(value);
|
||||||
|
}
|
||||||
|
void return_value(T &&value)
|
||||||
|
noexcept(std::is_nothrow_move_constructible_v<T>) {
|
||||||
|
this->set_value(std::move(value));
|
||||||
|
}
|
||||||
|
void unhandled_exception() noexcept {
|
||||||
|
this->set_exception(boost::current_exception());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same for std::future<void>.
|
||||||
|
template <typename... Args>
|
||||||
|
struct std::coroutine_traits<boost::future<void>, Args...> {
|
||||||
|
struct promise_type : boost::promise<void> {
|
||||||
|
boost::future<void> get_return_object() noexcept {
|
||||||
|
return this->get_future();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::suspend_never initial_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::suspend_never final_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_void() noexcept {
|
||||||
|
this->set_value();
|
||||||
|
}
|
||||||
|
void unhandled_exception() noexcept {
|
||||||
|
this->set_exception(boost::current_exception());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
struct std::coroutine_traits<void, Args...> {
|
||||||
struct promise_type {
|
struct promise_type {
|
||||||
auto initial_suspend() const noexcept {
|
void get_return_object() noexcept {}
|
||||||
std::cout << "initial_suspend" << std::endl;
|
|
||||||
return std::suspend_never();
|
std::suspend_never initial_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::suspend_never final_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto final_suspend() const noexcept {
|
void return_void() noexcept {}
|
||||||
std::cout << "final_suspend" << std::endl;
|
|
||||||
return std::suspend_never();
|
void unhandled_exception() noexcept {
|
||||||
|
try {
|
||||||
|
boost::rethrow_exception(boost::current_exception());
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto operator co_await(boost::future<T> future) noexcept requires(!std::is_reference_v<T>) {
|
||||||
|
|
||||||
|
struct awaiter : public boost::future<T> {
|
||||||
|
|
||||||
|
bool await_ready() const noexcept {
|
||||||
|
return this->is_ready();
|
||||||
|
}
|
||||||
|
void await_suspend(std::coroutine_handle<> cont) {
|
||||||
|
this->then([this, cont](boost::future<T>&& fut){
|
||||||
|
|
||||||
|
// future was moved out of awaiter to this lambda
|
||||||
|
// so, we need to bring it back
|
||||||
|
this->swap(fut);
|
||||||
|
|
||||||
|
// Resume coroutine on the main thread (i.e. GMainContext)
|
||||||
|
g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, awaiter::resumeContinuation, (gpointer)&cont, nullptr);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<T> get_return_object() {
|
T await_resume() {
|
||||||
std::cout << "get_return_object" << std::endl;
|
return this->get();
|
||||||
return Task<T>{ std::coroutine_handle<promise_type>::from_promise(*this) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void return_value(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>) {
|
static int resumeContinuation(void* data) {
|
||||||
std::cout << "return_value: " << value << std::endl;
|
auto handler = reinterpret_cast<std::coroutine_handle<>*>(data);
|
||||||
_value = value;
|
if(*handler) {
|
||||||
}
|
handler->resume();
|
||||||
|
}
|
||||||
|
|
||||||
// void return_void() {
|
return G_SOURCE_REMOVE;
|
||||||
// std::cout << "return_void" << std::endl;
|
|
||||||
// }
|
|
||||||
|
|
||||||
void unhandled_exception() {
|
|
||||||
std::cout << "unhandled_exception" << std::endl;
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
return awaiter{std::move(future)};
|
||||||
std::optional<T> _value = std::nullopt;
|
|
||||||
std::coroutine_handle<promise_type> _handle;
|
|
||||||
|
|
||||||
//public:
|
|
||||||
// bool await_ready() const noexcept {
|
|
||||||
// std::cout << "Awaiter::await_ready" << std::endl;
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void await_suspend(std::coroutine_handle<promise_type> handle) const {
|
|
||||||
// std::cout << "Awaiter::await_suspend" << std::endl;
|
|
||||||
// _handle = handle;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// T await_resume() {
|
|
||||||
// std::cout << "Awaiter::await_resume" << std::endl;
|
|
||||||
// return _value.value();
|
|
||||||
// }
|
|
||||||
|
|
||||||
public:
|
|
||||||
void set_result(T result) {
|
|
||||||
_value = result;
|
|
||||||
_handle();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename U> struct Awaiter {
|
|
||||||
public:
|
|
||||||
bool await_ready() const noexcept {
|
|
||||||
std::cout << "Awaiter::await_ready" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void await_suspend(std::coroutine_handle<typename Task<U>::promise_type> cont) const {
|
|
||||||
std::cout << "Awaiter::await_suspend" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
U await_resume() {
|
|
||||||
std::cout << "Awaiter::await_resume" << std::endl;
|
|
||||||
return _task.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Task<U> _task;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Awaiter(Task<U> task): _task(task) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
auto operator co_await(Task<T> task) noexcept /*requires(!std::is_reference_v<T>)*/ {
|
|
||||||
return Awaiter<T>(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //AUTOCAT_GNOME_TASK_H
|
#endif //AUTOCAT_GNOME_TASK_H
|
||||||
@ -3,8 +3,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "LoginWindow.h"
|
#include "LoginWindow.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
#include "../services/Api.h"
|
#include "../services/Api.h"
|
||||||
|
|
||||||
|
#include <gtkmm/application.h>
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -17,13 +19,13 @@ LoginWindow::LoginWindow() {
|
|||||||
_passwordField.set_input_purpose(Gtk::InputPurpose::PASSWORD);
|
_passwordField.set_input_purpose(Gtk::InputPurpose::PASSWORD);
|
||||||
_passwordField.set_visibility(false);
|
_passwordField.set_visibility(false);
|
||||||
|
|
||||||
_emailField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::ValidateFields));
|
_emailField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::validateFields));
|
||||||
_passwordField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::ValidateFields));
|
_passwordField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::validateFields));
|
||||||
|
|
||||||
_loginButton.set_margin_top(8);
|
_loginButton.set_margin_top(8);
|
||||||
_loginButton.set_margin_bottom(8);
|
_loginButton.set_margin_bottom(8);
|
||||||
_loginButton.set_label("Log in");
|
_loginButton.set_label("Log in");
|
||||||
_loginButton.signal_clicked().connect(sigc::mem_fun(*this, &LoginWindow::LoginClicked));
|
_loginButton.signal_clicked().connect(sigc::mem_fun(*this, &LoginWindow::loginClicked));
|
||||||
_loginButton.set_sensitive(false);
|
_loginButton.set_sensitive(false);
|
||||||
|
|
||||||
Gtk::Box box(Gtk::Orientation::VERTICAL, 8);
|
Gtk::Box box(Gtk::Orientation::VERTICAL, 8);
|
||||||
@ -38,32 +40,48 @@ LoginWindow::LoginWindow() {
|
|||||||
set_child(box);
|
set_child(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Task<int> foo() {
|
void LoginWindow::loginClicked() {
|
||||||
// co_return 42;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//Task<int> bar() {
|
|
||||||
// auto xxx = co_await foo();
|
|
||||||
// std::cout << "xxx: " << xxx << std::endl;
|
|
||||||
//}
|
|
||||||
|
|
||||||
void LoginWindow::LoginClicked() {
|
|
||||||
auto email = _emailField.get_text();
|
auto email = _emailField.get_text();
|
||||||
auto password = _passwordField.get_text();
|
auto password = _passwordField.get_text();
|
||||||
|
|
||||||
_loginButton.set_sensitive(false);
|
enableControls(false);
|
||||||
_emailField.set_sensitive(false);
|
|
||||||
_passwordField.set_sensitive(false);
|
|
||||||
_spinner.start();
|
_spinner.start();
|
||||||
|
|
||||||
std::cout << "Login clicked" << std::endl;
|
try {
|
||||||
std::cout << "Login: " << email << std::endl;
|
auto user = co_await Api::login(email, password);
|
||||||
std::cout << "Password: " << password << std::endl;
|
auto app = this->get_application();
|
||||||
|
auto mainWindow = new MainWindow();
|
||||||
User user = co_await Api::login(email, password);
|
mainWindow->show();
|
||||||
|
hide();
|
||||||
|
app->add_window(*mainWindow);
|
||||||
|
app->remove_window(*this);
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
enableControls(true);
|
||||||
|
_spinner.stop();
|
||||||
|
showError(ex.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoginWindow::ValidateFields() {
|
void LoginWindow::validateFields() {
|
||||||
bool buttonEnabled = _emailField.get_text_length() > 0 && _passwordField.get_text_length() > 0;
|
bool buttonEnabled = _emailField.get_text_length() > 0 && _passwordField.get_text_length() > 0;
|
||||||
_loginButton.set_sensitive(buttonEnabled);
|
_loginButton.set_sensitive(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.set_sensitive(enable);
|
||||||
|
_emailField.set_sensitive(enable);
|
||||||
|
_passwordField.set_sensitive(enable);
|
||||||
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@
|
|||||||
#include <gtkmm/entry.h>
|
#include <gtkmm/entry.h>
|
||||||
#include <gtkmm/button.h>
|
#include <gtkmm/button.h>
|
||||||
#include <gtkmm/spinner.h>
|
#include <gtkmm/spinner.h>
|
||||||
|
#include <gtkmm/messagedialog.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class LoginWindow: public Gtk::Window {
|
class LoginWindow: public Gtk::Window {
|
||||||
private:
|
private:
|
||||||
@ -16,13 +18,16 @@ private:
|
|||||||
Gtk::Entry _passwordField;
|
Gtk::Entry _passwordField;
|
||||||
Gtk::Button _loginButton;
|
Gtk::Button _loginButton;
|
||||||
Gtk::Spinner _spinner;
|
Gtk::Spinner _spinner;
|
||||||
|
std::unique_ptr<Gtk::MessageDialog> _dialog;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LoginWindow();
|
LoginWindow();
|
||||||
~LoginWindow() override = default;
|
~LoginWindow() override = default;
|
||||||
|
|
||||||
void LoginClicked();
|
void loginClicked();
|
||||||
void ValidateFields();
|
void validateFields();
|
||||||
|
void showError(const std::string& message);
|
||||||
|
void enableControls(bool enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,34 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
#include <gdkmm.h>
|
||||||
|
|
||||||
MainWindow::MainWindow() {
|
MainWindow::MainWindow() {
|
||||||
set_title("Main window");
|
//set_title("Main window");
|
||||||
set_default_size(640, 480);
|
set_default_size(640, 480);
|
||||||
|
//set_decorated(false);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,24 @@
|
|||||||
#ifndef AUTOCAT_GNOME_MAINWINDOW_H
|
#ifndef AUTOCAT_GNOME_MAINWINDOW_H
|
||||||
#define AUTOCAT_GNOME_MAINWINDOW_H
|
#define AUTOCAT_GNOME_MAINWINDOW_H
|
||||||
|
|
||||||
|
#include "TitleBar.h"
|
||||||
|
|
||||||
#include <gtkmm/window.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 Gtk::Window {
|
||||||
|
private:
|
||||||
|
TitleBar _titleBar;
|
||||||
|
Gtk::Paned _paned;
|
||||||
|
Gtk::Label _masterLabel;
|
||||||
|
Gtk::Label _detailLabel;
|
||||||
|
Gtk::Box _masterBox;
|
||||||
|
Gtk::Box _detailBox;
|
||||||
|
Glib::RefPtr<Gtk::SizeGroup> _sizeGroup;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MainWindow();
|
MainWindow();
|
||||||
};
|
};
|
||||||
|
|||||||
21
gui/TitleBar.cpp
Normal file
21
gui/TitleBar.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
23
gui/TitleBar.h
Normal file
23
gui/TitleBar.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
23
main.cpp
23
main.cpp
@ -1,8 +1,29 @@
|
|||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/LoginWindow.h"
|
#include "gui/LoginWindow.h"
|
||||||
|
#include "services/Settings.h"
|
||||||
|
#include "app/App.h"
|
||||||
#include <gtkmm/application.h>
|
#include <gtkmm/application.h>
|
||||||
|
#include <glibmm.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
auto app = Gtk::Application::create("pro.aliencat.aliencat");
|
auto app = Gtk::Application::create("pro.aliencat.aliencat");
|
||||||
return app->make_window_and_run<LoginWindow>(argc, argv);
|
auto window = createStartWindow();
|
||||||
|
|
||||||
|
app->signal_activate().connect([&](){
|
||||||
|
app->add_window(*window);
|
||||||
|
window->show();
|
||||||
|
});
|
||||||
|
return app->run(argc, argv);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
|
|
||||||
User::User(std::string_view login, std::string_view token): login(login), token(token) {
|
User::User(std::string_view email, std::string_view token): email(email), token(token) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,16 +7,21 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
public:
|
public:
|
||||||
std::string login;
|
std::string email;
|
||||||
std::string token;
|
std::string token;
|
||||||
std::optional<std::string> googleIdToken;
|
std::optional<std::string> googleIdToken;
|
||||||
std::optional<std::string> googleRefreshToken;
|
std::optional<std::string> googleRefreshToken;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
User(std::string_view login, std::string_view token);
|
User() = default;
|
||||||
|
User(const User& user) = default;
|
||||||
|
User(std::string_view email, std::string_view token);
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(User, email, token)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,47 +3,76 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "Api.h"
|
#include "Api.h"
|
||||||
|
#include "Settings.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
template<class...Args>
|
template<class...Args>
|
||||||
struct callback {
|
struct Callback {
|
||||||
void(*function)(Args..., void*) = nullptr;
|
void(*function)(Args..., void*) = nullptr;
|
||||||
std::unique_ptr<void, void(*)(void*)> state;
|
void* state = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args, typename Lambda>
|
template<typename... Args, typename Lambda>
|
||||||
callback<Args...> voidify(Lambda&& l) {
|
Callback<Args...> voidify(Lambda&& l) {
|
||||||
using Func = typename std::decay<Lambda>::type;
|
using Func = typename std::decay<Lambda>::type;
|
||||||
std::unique_ptr<void, void(*)(void*)> data(
|
auto data = new Func(std::forward<Lambda>(l));
|
||||||
new Func(std::forward<Lambda>(l)),
|
|
||||||
+[](void* ptr){
|
|
||||||
std::cout << "!!!!!!!!!!!!!!!!! Deleting data" << std::endl;
|
|
||||||
delete (Func*)ptr;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
+[](Args... args, void* v)->void {
|
+[](Args... args, void* v)->void {
|
||||||
Func* f = static_cast< Func* >(v);
|
Func* f = static_cast< Func* >(v);
|
||||||
(*f)(std::forward<Args>(args)...);
|
(*f)(std::forward<Args>(args)...);
|
||||||
|
delete f;
|
||||||
},
|
},
|
||||||
std::move(data)
|
data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
|
const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
|
||||||
SoupSession* Api::_session = soup_session_new();
|
SoupSession* Api::_session = soup_session_new();
|
||||||
|
|
||||||
Task<User> Api::login(std::string email, std::string password) {
|
template<typename T>
|
||||||
std::string url = _baseUrl + "user/login";
|
boost::future<T> Api::post(const std::string &method, const nlohmann::json& params) {
|
||||||
|
std::string url = _baseUrl + method;
|
||||||
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
|
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
|
||||||
|
|
||||||
auto task = Task<User>();
|
auto promise = std::make_shared<boost::promise<T>>();
|
||||||
|
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
|
||||||
auto callback = voidify<SoupSession*, SoupMessage*>([&](SoupSession* session, SoupMessage* message) {
|
if(message->status_code >= 200 && message->status_code < 300) {
|
||||||
std::cout << "Callback called" << std::endl;
|
auto responseString = std::string(message->response_body->data, message->response_body->length);
|
||||||
task.set_result(User("qwe", "asdf"));
|
auto json = nlohmann::json::parse(responseString);
|
||||||
|
if(json["success"].get<bool>()) {
|
||||||
|
std::cout << "response: " << responseString << std::endl;
|
||||||
|
auto user = json["data"].get<T>();
|
||||||
|
promise->set_value(user);
|
||||||
|
} else {
|
||||||
|
auto error = json["error"].get<std::string>();
|
||||||
|
promise->set_exception(FutureException(error));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
promise->set_exception(FutureException(message->reason_phrase));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
soup_session_queue_message(_session, msg, callback.function, callback.state.get());
|
|
||||||
|
|
||||||
return task;
|
auto jsonStr = params.dump();
|
||||||
|
|
||||||
|
soup_message_set_request(msg, "application/json", SOUP_MEMORY_COPY, jsonStr.c_str(), jsonStr.size());
|
||||||
|
soup_session_queue_message(_session, msg, callback.function, callback.state);
|
||||||
|
|
||||||
|
return promise->get_future();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::future<User> Api::login(std::string email, std::string password) {
|
||||||
|
|
||||||
|
nlohmann::json params = {
|
||||||
|
{ "email", email },
|
||||||
|
{ "password", password }
|
||||||
|
};
|
||||||
|
|
||||||
|
auto user = Settings::instance().user();
|
||||||
|
auto result = co_await post<User>("user/login", params);
|
||||||
|
|
||||||
|
user.email = result.email;
|
||||||
|
user.token = result.token;
|
||||||
|
Settings::instance().setUser(user);
|
||||||
|
|
||||||
|
co_return user;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <libsoup/soup.h>
|
#include <libsoup/soup.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "../models/User.h"
|
#include "../models/User.h"
|
||||||
#include "../coro/Task.h"
|
#include "../coro/Task.h"
|
||||||
|
|
||||||
@ -15,8 +17,12 @@ private:
|
|||||||
static const std::string _baseUrl;
|
static const std::string _baseUrl;
|
||||||
static SoupSession* _session;
|
static SoupSession* _session;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
static boost::future<T> post(const std::string& method, const nlohmann::json& params);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Task<User> login(std::string email, std::string password);
|
static boost::future<User> login(std::string email, std::string password);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
71
services/Settings.cpp
Normal file
71
services/Settings.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 09.01.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
fs::path getDataPath() {
|
||||||
|
auto dataDir = std::getenv("XDG_DATA_HOME");
|
||||||
|
if(dataDir) {
|
||||||
|
return fs::path(dataDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto home = std::getenv("HOME");
|
||||||
|
if(home) {
|
||||||
|
return fs::path(home) / ".local" / "share";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Failed to find data home directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string readString(const fs::path& path) {
|
||||||
|
std::ifstream stream(path);
|
||||||
|
stream.seekg(0, std::ios::end);
|
||||||
|
auto size = stream.tellg();
|
||||||
|
std::string buffer(size, ' ');
|
||||||
|
stream.seekg(0);
|
||||||
|
stream.read(&buffer[0], size);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeString(const fs::path& path, const std::string& text) {
|
||||||
|
std::ofstream stream(path);
|
||||||
|
stream << text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings Settings::instance() {
|
||||||
|
static Settings instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::Settings() {
|
||||||
|
_dataPath = getDataPath() / "autocat";
|
||||||
|
if(!fs::exists(_dataPath)) {
|
||||||
|
fs::create_directories(_dataPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
User Settings::user() const {
|
||||||
|
auto userFile = _dataPath / "user.json";
|
||||||
|
if(fs::exists(userFile)) {
|
||||||
|
auto jsonText = readString(userFile);
|
||||||
|
std::cout << "User json text: " << jsonText << std::endl;
|
||||||
|
return nlohmann::json::parse(jsonText).get<User>();
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setUser(const User &user) {
|
||||||
|
auto userFile = _dataPath / "user.json";
|
||||||
|
nlohmann::json json = user;
|
||||||
|
auto userString = json.dump(4);
|
||||||
|
writeString(userFile, userString);
|
||||||
|
}
|
||||||
24
services/Settings.h
Normal file
24
services/Settings.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 09.01.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef AUTOCAT_GNOME_SETTINGS_H
|
||||||
|
#define AUTOCAT_GNOME_SETTINGS_H
|
||||||
|
|
||||||
|
#include "../models/User.h"
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
class Settings {
|
||||||
|
private:
|
||||||
|
std::filesystem::path _dataPath;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Settings instance();
|
||||||
|
Settings();
|
||||||
|
|
||||||
|
[[nodiscard]] User user() const;
|
||||||
|
void setUser(const User& user);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //AUTOCAT_GNOME_SETTINGS_H
|
||||||
Loading…
Reference in New Issue
Block a user