From 530352f28b919ee3a55d22585c16c1661e8ca2ca Mon Sep 17 00:00:00 2001
From: Stanislaw Halik <sthalik@misaki.pl>
Date: Wed, 10 Aug 2016 18:15:45 +0200
Subject: gui/main-window: add a tray menu

Issue: #410
---
 gui/main-window.cpp | 98 ++++++++++++++++++++++++++++++++++++++++++++++-------
 gui/main-window.hpp | 46 +++++++++++++++----------
 2 files changed, 115 insertions(+), 29 deletions(-)

(limited to 'gui')

diff --git a/gui/main-window.cpp b/gui/main-window.cpp
index e2a67e80..667c3032 100644
--- a/gui/main-window.cpp
+++ b/gui/main-window.cpp
@@ -15,11 +15,16 @@
 #include <QFileDialog>
 #include <QDesktopServices>
 #include <QCoreApplication>
+#include <QIcon>
+#include <QString>
+#include <QChar>
 
 #ifdef _WIN32
 #   include <windows.h>
 #endif
 
+extern "C" const char* opentrack_version;
+
 MainWindow::MainWindow() :
     State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH),
     pose_update_timer(this),
@@ -123,6 +128,8 @@ MainWindow::MainWindow() :
 
     ui.btnStartTracker->setFocus();
 
+    init_tray_menu();
+
     connect(&s.tray_enabled,
             static_cast<void (base_value::*)(bool)>(&base_value::valueChanged),
             this,
@@ -130,6 +137,63 @@ MainWindow::MainWindow() :
     ensure_tray();
 }
 
+void MainWindow::init_tray_menu()
+{
+    tray_menu.clear();
+
+    QString display_name(opentrack_version);
+    if (display_name.startsWith("opentrack-"))
+        display_name.replace(sizeof("opentrack") - 1, 1, QChar(' '));
+    if (display_name.endsWith("-DEBUG"))
+        display_name.replace(display_name.size() - int(sizeof("DEBUG")), display_name.size(), " (debug)");
+
+    menu_action_header.setEnabled(false);
+    menu_action_header.setText(display_name);
+    menu_action_header.setIcon(QIcon(":/images/facetracknoir.png"));
+    tray_menu.addAction(&menu_action_header);
+
+    menu_action_show.setIconVisibleInMenu(true);
+    menu_action_show.setText(isHidden() ? "Show the Octopus" : "Hide the Octopus");
+    menu_action_show.setIcon(QIcon(":/images/facetracknoir.png"));
+    QObject::connect(&menu_action_show, &QAction::triggered, this, [&]() { toggle_restore_from_tray(QSystemTrayIcon::Trigger); });
+    tray_menu.addAction(&menu_action_show);
+
+    tray_menu.addSeparator();
+
+    menu_action_tracker.setText("Tracker settings");
+    menu_action_tracker.setIcon(QIcon(":/images/tools.png"));
+    QObject::connect(&menu_action_tracker, &QAction::triggered, this, &MainWindow::showTrackerSettings);
+    tray_menu.addAction(&menu_action_tracker);
+
+    menu_action_filter.setText("Filter settings");
+    menu_action_filter.setIcon(QIcon(":/images/filter-16.png"));
+    QObject::connect(&menu_action_filter, &QAction::triggered, this, &MainWindow::showFilterSettings);
+    tray_menu.addAction(&menu_action_filter);
+
+    menu_action_proto.setText("Protocol settings");
+    menu_action_proto.setIcon(QIcon(":/images/settings16.png"));
+    QObject::connect(&menu_action_proto, &QAction::triggered, this, &MainWindow::showProtocolSettings);
+    tray_menu.addAction(&menu_action_proto);
+
+    tray_menu.addSeparator();
+
+    menu_action_mappings.setIcon(QIcon(":/images/curves.png"));
+    menu_action_mappings.setText("Mappings");
+    QObject::connect(&menu_action_mappings, &QAction::triggered, this, &MainWindow::showCurveConfiguration);
+    tray_menu.addAction(&menu_action_mappings);
+
+    menu_action_options.setIcon(QIcon(":/images/tools.png"));
+    menu_action_options.setText("Options");
+    QObject::connect(&menu_action_options, &QAction::triggered, this, &MainWindow::show_options_dialog);
+    tray_menu.addAction(&menu_action_options);
+
+    tray_menu.addSeparator();
+
+    menu_action_exit.setText("Exit");
+    QObject::connect(&menu_action_exit, &QAction::triggered, this, &MainWindow::exit);
+    tray_menu.addAction(&menu_action_exit);
+}
+
 void MainWindow::register_shortcuts()
 {
     using t_key = Shortcuts::t_key;
@@ -237,8 +301,6 @@ void MainWindow::open_config_directory()
     }
 }
 
-extern "C" const char* opentrack_version;
-
 void MainWindow::refresh_config_list()
 {
     if (work)
@@ -394,8 +456,8 @@ void MainWindow::display_pose(const double *mapped, const double *raw)
 
     for (int i = 0; i < 6; i++)
     {
-        mapped_[i] = (int) mapped[i];
-        raw_[i] = (int) raw[i];
+        mapped_[i] = int(mapped[i]);
+        raw_[i] = int(raw[i]);
     }
 
     ui.raw_x->display(raw_[TX]);
@@ -440,7 +502,7 @@ void MainWindow::showHeadPose()
 }
 
 template<typename t>
-bool mk_dialog(mem<dylib> lib, mem<t>& orig)
+bool mk_dialog(mem<dylib> lib, ptr<t>& orig)
 {
     if (orig && orig->isVisible())
     {
@@ -451,14 +513,14 @@ bool mk_dialog(mem<dylib> lib, mem<t>& orig)
 
     if (lib && lib->Dialog)
     {
-        auto dialog = mem<t>(reinterpret_cast<t*>(lib->Dialog()));
+        t* dialog = reinterpret_cast<t*>(lib->Dialog());
         dialog->setWindowFlags(Qt::Dialog);
         dialog->setFixedSize(dialog->size());
 
-        orig = dialog;
+        orig.reset(dialog);
         dialog->show();
 
-        QObject::connect(dialog.get(), &plugin_api::detail::BaseDialog::closing, [&]() -> void { orig = nullptr; });
+        QObject::connect(dialog, &plugin_api::detail::BaseDialog::closing, [&]() -> void { orig = nullptr; });
 
         return true;
     }
@@ -485,7 +547,7 @@ void MainWindow::showFilterSettings()
 }
 
 template<typename t, typename... Args>
-bool mk_window(mem<t>* place, Args&&... params)
+static bool mk_window(ptr<t>* place, Args&&... params)
 {
     if (*place && (*place)->isVisible())
     {
@@ -495,7 +557,7 @@ bool mk_window(mem<t>* place, Args&&... params)
     }
     else
     {
-        *place = std::make_shared<t>(std::forward<Args>(params)...);
+        *place = make_unique<t>(std::forward<Args>(params)...);
         (*place)->setWindowFlags(Qt::Dialog);
         (*place)->show();
         return true;
@@ -547,8 +609,9 @@ void MainWindow::ensure_tray()
     {
         if (!tray)
         {
-            tray = std::make_shared<QSystemTrayIcon>(this);
+            tray = make_unique<QSystemTrayIcon>(this);
             tray->setIcon(QIcon(":/images/facetracknoir.png"));
+            tray->setContextMenu(&tray_menu);
             tray->show();
 
             connect(tray.get(),
@@ -591,6 +654,8 @@ void MainWindow::toggle_restore_from_tray(QSystemTrayIcon::ActivationReason e)
 
     const bool is_minimized = isHidden() || !is_tray_enabled();
 
+    menu_action_show.setText(!isHidden() ? "Show the Octopus" : "Hide the Octopus");
+
     setVisible(is_minimized);
     setHidden(!is_minimized);
 
@@ -611,7 +676,9 @@ void MainWindow::toggle_restore_from_tray(QSystemTrayIcon::ActivationReason e)
 
 bool MainWindow::maybe_hide_to_tray(QEvent* e)
 {
-    if (e->type() == QEvent::WindowStateChange && is_tray_enabled())
+    if (e->type() == QEvent::WindowStateChange &&
+        (windowState() & Qt::WindowMinimized) &&
+        is_tray_enabled())
     {
         e->accept();
         ensure_tray();
@@ -661,7 +728,14 @@ void MainWindow::changeEvent(QEvent* e)
     if (maybe_hide_to_tray(e))
         e->accept();
     else
+    {
         QMainWindow::changeEvent(e);
+    }
+}
+
+void MainWindow::closeEvent(QCloseEvent*)
+{
+    exit();
 }
 
 bool MainWindow::is_tray_enabled()
diff --git a/gui/main-window.hpp b/gui/main-window.hpp
index 14c7cf0d..b068d158 100644
--- a/gui/main-window.hpp
+++ b/gui/main-window.hpp
@@ -8,6 +8,17 @@
 
 #pragma once
 
+#include "opentrack/plugin-support.hpp"
+#include "mapping-window.hpp"
+#include "options-dialog.hpp"
+#include "process_detector.h"
+#include "opentrack-logic/main-settings.hpp"
+#include "opentrack-logic/tracker.h"
+#include "opentrack-logic/shortcuts.h"
+#include "opentrack-logic/work.hpp"
+#include "opentrack-logic/state.hpp"
+#include "opentrack-compat/options.hpp"
+
 #include <QMainWindow>
 #include <QKeySequence>
 #include <QShortcut>
@@ -16,24 +27,16 @@
 #include <QSystemTrayIcon>
 #include <QString>
 #include <QMenu>
+#include <QAction>
 #include <QEvent>
+#include <QCloseEvent>
 
 #include <vector>
 #include <tuple>
+#include <memory>
 
 #include "ui_main-window.h"
 
-#include "opentrack-compat/options.hpp"
-#include "opentrack-logic/main-settings.hpp"
-#include "opentrack/plugin-support.hpp"
-#include "opentrack-logic/tracker.h"
-#include "opentrack-logic/shortcuts.h"
-#include "opentrack-logic/work.hpp"
-#include "opentrack-logic/state.hpp"
-#include "mapping-window.hpp"
-#include "options-dialog.hpp"
-#include "process_detector.h"
-
 using namespace options;
 
 class MainWindow : public QMainWindow, private State
@@ -44,18 +47,24 @@ class MainWindow : public QMainWindow, private State
 
     Shortcuts global_shortcuts;
     module_settings m;
-    mem<QSystemTrayIcon> tray;
+    ptr<QSystemTrayIcon> tray;
+    QMenu tray_menu;
     QTimer pose_update_timer;
     QTimer det_timer;
     QTimer config_list_timer;
-    mem<OptionsDialog> options_widget;
-    mem<MapWidget> mapping_widget;
+    ptr<OptionsDialog> options_widget;
+    ptr<MapWidget> mapping_widget;
     QShortcut kbd_quit;
-    mem<IFilterDialog> pFilterDialog;
-    mem<IProtocolDialog> pProtocolDialog;
-    mem<ITrackerDialog> pTrackerDialog;
+    ptr<IFilterDialog> pFilterDialog;
+    ptr<IProtocolDialog> pProtocolDialog;
+    ptr<ITrackerDialog> pTrackerDialog;
     process_detector_worker det;
     QMenu profile_menu;
+
+    QAction menu_action_header, menu_action_show, menu_action_exit,
+            menu_action_tracker, menu_action_filter, menu_action_proto,
+            menu_action_options, menu_action_mappings;
+
     bool is_refreshing_profiles;
 
     mem<dylib> current_tracker()
@@ -82,7 +91,10 @@ class MainWindow : public QMainWindow, private State
     void register_shortcuts();
     void set_keys_enabled(bool flag);
 
+    void init_tray_menu();
+
     void changeEvent(QEvent* e) override;
+    void closeEvent(QCloseEvent*) override;
     bool maybe_hide_to_tray(QEvent* e);
 
 private slots:
-- 
cgit v1.2.3