system tray support
Some checks are pending
ContinuousDelivery / linux-ubuntu (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousDelivery / windows (push) Waiting to run
ContinuousDelivery / windows-asan (push) Waiting to run
ContinuousDelivery / release (push) Blocked by required conditions
ContinuousIntegration / linux (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousIntegration / macos (push) Waiting to run
ContinuousIntegration / windows (push) Waiting to run
Some checks are pending
ContinuousDelivery / linux-ubuntu (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousDelivery / windows (push) Waiting to run
ContinuousDelivery / windows-asan (push) Waiting to run
ContinuousDelivery / release (push) Blocked by required conditions
ContinuousIntegration / linux (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousIntegration / macos (push) Waiting to run
ContinuousIntegration / windows (push) Waiting to run
This commit is contained in:
parent
859ad7df81
commit
2fa116b76b
@ -65,6 +65,8 @@
|
|||||||
|
|
||||||
pipewire
|
pipewire
|
||||||
|
|
||||||
|
libayatana-appindicator
|
||||||
|
|
||||||
# sdl3_image:
|
# sdl3_image:
|
||||||
libpng
|
libpng
|
||||||
libjpeg
|
libjpeg
|
||||||
|
@ -3,7 +3,12 @@
|
|||||||
"save_file_path": "tomato.tox"
|
"save_file_path": "tomato.tox"
|
||||||
},
|
},
|
||||||
"tomato": {
|
"tomato": {
|
||||||
"skip_setup_and_load": true
|
"skip_setup_and_load": true,
|
||||||
|
|
||||||
|
"system_tray": true,
|
||||||
|
|
||||||
|
"start_to_tray": true,
|
||||||
|
"__comment_start_to_tray": "Implies both skip_setup_and_load and system_tray"
|
||||||
},
|
},
|
||||||
"PluginManager": {
|
"PluginManager": {
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -71,6 +71,9 @@ target_sources(tomato PUBLIC
|
|||||||
./sdl_clipboard_utils.hpp
|
./sdl_clipboard_utils.hpp
|
||||||
./sdl_clipboard_utils.cpp
|
./sdl_clipboard_utils.cpp
|
||||||
|
|
||||||
|
./sys_tray.hpp
|
||||||
|
./sys_tray.cpp
|
||||||
|
|
||||||
./chat_gui/theme.hpp
|
./chat_gui/theme.hpp
|
||||||
./chat_gui/theme.cpp
|
./chat_gui/theme.cpp
|
||||||
./chat_gui/icons/direct.hpp
|
./chat_gui/icons/direct.hpp
|
||||||
|
@ -16,6 +16,22 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
static std::unique_ptr<SystemTray> constructSystemTray(SimpleConfigModel& conf, SDL_Window* window) {
|
||||||
|
bool conf_system_tray {false};
|
||||||
|
if (auto value_stt = conf.get_bool("tomato", "start_to_tray"); value_stt.has_value) {
|
||||||
|
conf_system_tray = value_stt.value();
|
||||||
|
// TODO: warn or error if "system_try" is false
|
||||||
|
} else if (auto value_st = conf.get_bool("tomato", "system_tray"); value_st.has_value) {
|
||||||
|
conf_system_tray = value_st.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf_system_tray) {
|
||||||
|
return std::make_unique<SystemTray>(window);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme& theme_, std::string save_path, std::string save_password, std::string new_username, std::vector<std::string> plugins) :
|
MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme& theme_, std::string save_path, std::string save_password, std::string new_username, std::vector<std::string> plugins) :
|
||||||
renderer(renderer_),
|
renderer(renderer_),
|
||||||
conf(std::move(conf_)),
|
conf(std::move(conf_)),
|
||||||
@ -43,7 +59,8 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme
|
|||||||
contact_tc(tal, sdlrtu),
|
contact_tc(tal, sdlrtu),
|
||||||
mil(),
|
mil(),
|
||||||
msg_tc(mil, sdlrtu),
|
msg_tc(mil, sdlrtu),
|
||||||
si(rmm, cr, SDL_GetRenderWindow(renderer_)),
|
st(constructSystemTray(conf, SDL_GetRenderWindow(renderer_))),
|
||||||
|
si(rmm, cr, SDL_GetRenderWindow(renderer_), st.get()),
|
||||||
cg(conf, os, rmm, cr, sdlrtu, contact_tc, msg_tc, theme),
|
cg(conf, os, rmm, cr, sdlrtu, contact_tc, msg_tc, theme),
|
||||||
sw(conf),
|
sw(conf),
|
||||||
osui(os),
|
osui(os),
|
||||||
@ -248,13 +265,13 @@ bool MainScreen::handleEvent(SDL_Event& e) {
|
|||||||
if (e.type == SDL_EVENT_DROP_FILE) {
|
if (e.type == SDL_EVENT_DROP_FILE) {
|
||||||
std::cout << "DROP FILE: " << e.drop.data << "\n";
|
std::cout << "DROP FILE: " << e.drop.data << "\n";
|
||||||
_dopped_files.emplace_back(e.drop.data);
|
_dopped_files.emplace_back(e.drop.data);
|
||||||
//cg.sendFilePath(e.drop.data);
|
|
||||||
_render_interval = 1.f/60.f; // TODO: magic
|
_render_interval = 1.f/60.f; // TODO: magic
|
||||||
_time_since_event = 0.f;
|
_time_since_event = 0.f;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED ||
|
||||||
e.type == SDL_EVENT_WINDOW_MINIMIZED ||
|
e.type == SDL_EVENT_WINDOW_MINIMIZED ||
|
||||||
e.type == SDL_EVENT_WINDOW_HIDDEN ||
|
e.type == SDL_EVENT_WINDOW_HIDDEN ||
|
||||||
e.type == SDL_EVENT_WINDOW_OCCLUDED // does this trigger on partial occlusion?
|
e.type == SDL_EVENT_WINDOW_OCCLUDED // does this trigger on partial occlusion?
|
||||||
@ -262,6 +279,11 @@ bool MainScreen::handleEvent(SDL_Event& e) {
|
|||||||
auto* window = SDL_GetWindowFromID(e.window.windowID);
|
auto* window = SDL_GetWindowFromID(e.window.windowID);
|
||||||
auto* event_renderer = SDL_GetRenderer(window);
|
auto* event_renderer = SDL_GetRenderer(window);
|
||||||
if (event_renderer != nullptr && event_renderer == renderer) {
|
if (event_renderer != nullptr && event_renderer == renderer) {
|
||||||
|
if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
|
||||||
|
// this event only fires if not last window (systrays count as windows)
|
||||||
|
SDL_HideWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
// our window is now obstructed
|
// our window is now obstructed
|
||||||
if (_window_hidden_ts < e.window.timestamp) {
|
if (_window_hidden_ts < e.window.timestamp) {
|
||||||
_window_hidden_ts = e.window.timestamp;
|
_window_hidden_ts = e.window.timestamp;
|
||||||
@ -269,6 +291,9 @@ bool MainScreen::handleEvent(SDL_Event& e) {
|
|||||||
//std::cout << "TOMAT: window hidden " << e.type << " " << e.window.timestamp << "\n";
|
//std::cout << "TOMAT: window hidden " << e.type << " " << e.window.timestamp << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (st) {
|
||||||
|
st->update();
|
||||||
|
}
|
||||||
return true; // forward?
|
return true; // forward?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +321,9 @@ bool MainScreen::handleEvent(SDL_Event& e) {
|
|||||||
}
|
}
|
||||||
_render_interval = 1.f/60.f; // TODO: magic
|
_render_interval = 1.f/60.f; // TODO: magic
|
||||||
_time_since_event = 0.f;
|
_time_since_event = 0.f;
|
||||||
|
if (st) {
|
||||||
|
st->update(); // TODO: limit this
|
||||||
|
}
|
||||||
return true; // forward?
|
return true; // forward?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "./tox_avatar_loader.hpp"
|
#include "./tox_avatar_loader.hpp"
|
||||||
#include "./message_image_loader.hpp"
|
#include "./message_image_loader.hpp"
|
||||||
|
|
||||||
|
#include "./sys_tray.hpp"
|
||||||
#include "./status_indicator.hpp"
|
#include "./status_indicator.hpp"
|
||||||
#include "./chat_gui4.hpp"
|
#include "./chat_gui4.hpp"
|
||||||
#include "./chat_gui/settings_window.hpp"
|
#include "./chat_gui/settings_window.hpp"
|
||||||
@ -92,6 +93,7 @@ struct MainScreen final : public Screen {
|
|||||||
MessageImageLoader mil;
|
MessageImageLoader mil;
|
||||||
TextureCache<void*, Message3Handle, MessageImageLoader> msg_tc;
|
TextureCache<void*, Message3Handle, MessageImageLoader> msg_tc;
|
||||||
|
|
||||||
|
std::unique_ptr<SystemTray> st;
|
||||||
StatusIndicator si;
|
StatusIndicator si;
|
||||||
ChatGui4 cg;
|
ChatGui4 cg;
|
||||||
SettingsWindow sw;
|
SettingsWindow sw;
|
||||||
|
@ -311,7 +311,13 @@ Screen* StartScreen::render(float, bool&) {
|
|||||||
|
|
||||||
// TODO: dont check every frame
|
// TODO: dont check every frame
|
||||||
// do in tick instead?
|
// do in tick instead?
|
||||||
if (_conf.get_bool("tomato", "skip_setup_and_load").value_or(false)) {
|
const bool start_to_tray = _conf.get_bool("tomato", "start_to_tray").value_or(false);
|
||||||
|
const bool skip_setup = _conf.get_bool("tomato", "skip_setup_and_load").value_or(false);
|
||||||
|
if (start_to_tray || skip_setup) {
|
||||||
|
if (start_to_tray) {
|
||||||
|
// TODO: the window should really be created hidden in the first place
|
||||||
|
SDL_HideWindow(SDL_GetRenderWindow(_renderer));
|
||||||
|
}
|
||||||
auto new_screen = std::make_unique<MainScreen>(std::move(_conf), _renderer, _theme, _tox_profile_path, _password, _user_name, queued_plugin_paths);
|
auto new_screen = std::make_unique<MainScreen>(std::move(_conf), _renderer, _theme, _tox_profile_path, _password, _user_name, queued_plugin_paths);
|
||||||
return new_screen.release();
|
return new_screen.release();
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,16 +28,22 @@ void StatusIndicator::updateState(State state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetWindowIcon(_main_window, surf.get());
|
SDL_SetWindowIcon(_main_window, surf.get());
|
||||||
|
|
||||||
|
if (_tray != nullptr) {
|
||||||
|
_tray->setIcon(surf.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusIndicator::StatusIndicator(
|
StatusIndicator::StatusIndicator(
|
||||||
RegistryMessageModelI& rmm,
|
RegistryMessageModelI& rmm,
|
||||||
Contact3Registry& cr,
|
Contact3Registry& cr,
|
||||||
SDL_Window* main_window
|
SDL_Window* main_window,
|
||||||
|
SystemTray* tray
|
||||||
) :
|
) :
|
||||||
_rmm(rmm),
|
_rmm(rmm),
|
||||||
_cr(cr),
|
_cr(cr),
|
||||||
_main_window(main_window)
|
_main_window(main_window),
|
||||||
|
_tray(tray)
|
||||||
{
|
{
|
||||||
// start off with base icon
|
// start off with base icon
|
||||||
updateState(State::base);
|
updateState(State::base);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <solanaceae/message3/registry_message_model.hpp>
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
#include "./sys_tray.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
// service that sets window and tray icon depending on program state
|
// service that sets window and tray icon depending on program state
|
||||||
@ -11,7 +13,7 @@ class StatusIndicator {
|
|||||||
Contact3Registry& _cr;
|
Contact3Registry& _cr;
|
||||||
|
|
||||||
SDL_Window* _main_window;
|
SDL_Window* _main_window;
|
||||||
// systray ptr here
|
SystemTray* _tray;
|
||||||
|
|
||||||
float _cooldown {1.f};
|
float _cooldown {1.f};
|
||||||
|
|
||||||
@ -27,7 +29,8 @@ class StatusIndicator {
|
|||||||
StatusIndicator(
|
StatusIndicator(
|
||||||
RegistryMessageModelI& rmm,
|
RegistryMessageModelI& rmm,
|
||||||
Contact3Registry& cr,
|
Contact3Registry& cr,
|
||||||
SDL_Window* main_window
|
SDL_Window* main_window,
|
||||||
|
SystemTray* tray = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
// does not actually render, just on the render thread
|
// does not actually render, just on the render thread
|
||||||
|
100
src/sys_tray.cpp
Normal file
100
src/sys_tray.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "./sys_tray.hpp"
|
||||||
|
|
||||||
|
#include "./icon_generator.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
SystemTray::SystemTray(SDL_Window* main_window) : _main_window(main_window) {
|
||||||
|
std::cout << "ST: adding system tray\n";
|
||||||
|
|
||||||
|
_tray = SDL_CreateTray(nullptr, "tomato");
|
||||||
|
if (_tray == nullptr) {
|
||||||
|
//std::cerr << "ST: failed to create SystemTray: " << SDL_GetError() << "\n";
|
||||||
|
throw std::runtime_error(std::string{"ST: failed to create SystemTray: "} + SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* tray_menu = SDL_CreateTrayMenu(_tray);
|
||||||
|
{
|
||||||
|
auto* entry_quit = SDL_InsertTrayEntryAt(tray_menu, 0, "Quit Tomato", SDL_TRAYENTRY_BUTTON);
|
||||||
|
SDL_SetTrayEntryCallback(entry_quit,
|
||||||
|
+[](void*, SDL_TrayEntry*){
|
||||||
|
// this is thread safe and triggers the shutdown in the main thread
|
||||||
|
SDL_Event quit_event;
|
||||||
|
quit_event.quit = {
|
||||||
|
SDL_EVENT_QUIT,
|
||||||
|
0,
|
||||||
|
SDL_GetTicksNS(),
|
||||||
|
};
|
||||||
|
SDL_PushEvent(&quit_event);
|
||||||
|
}
|
||||||
|
, nullptr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
_entry_showhide = SDL_InsertTrayEntryAt(tray_menu, 0, "Hide Tomato", SDL_TRAYENTRY_BUTTON);
|
||||||
|
SDL_SetTrayEntryCallback(_entry_showhide,
|
||||||
|
+[](void* userdata, SDL_TrayEntry*){
|
||||||
|
SDL_HideWindow(static_cast<SystemTray*>(userdata)->_main_window);
|
||||||
|
}
|
||||||
|
, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemTray::~SystemTray(void) {
|
||||||
|
if (_tray != nullptr) {
|
||||||
|
SDL_DestroyTray(_tray);
|
||||||
|
_tray = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemTray::setIcon(SDL_Surface* surf) {
|
||||||
|
if (_tray == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetTrayIcon(_tray, surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemTray::setStatusText(const std::string& status) {
|
||||||
|
if (_tray == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entry_status == nullptr) {
|
||||||
|
_entry_status = SDL_InsertTrayEntryAt(SDL_GetTrayMenu(_tray), 0, status.c_str(), SDL_TRAYENTRY_DISABLED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetTrayEntryLabel(_entry_status, status.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemTray::update(void) {
|
||||||
|
if (_tray == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entry_showhide != nullptr) {
|
||||||
|
// check if window is open and adjust text and callback
|
||||||
|
const bool hidden {(SDL_GetWindowFlags(_main_window) & SDL_WINDOW_HIDDEN) > 0};
|
||||||
|
// TODO: cache state
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
SDL_SetTrayEntryLabel(_entry_showhide, "Show Tomato");
|
||||||
|
SDL_SetTrayEntryCallback(_entry_showhide,
|
||||||
|
+[](void* userdata, SDL_TrayEntry*){
|
||||||
|
SDL_ShowWindow(static_cast<SystemTray*>(userdata)->_main_window);
|
||||||
|
}
|
||||||
|
, this);
|
||||||
|
} else {
|
||||||
|
SDL_SetTrayEntryLabel(_entry_showhide, "Hide Tomato");
|
||||||
|
SDL_SetTrayEntryCallback(_entry_showhide,
|
||||||
|
+[](void* userdata, SDL_TrayEntry*){
|
||||||
|
SDL_HideWindow(static_cast<SystemTray*>(userdata)->_main_window);
|
||||||
|
}
|
||||||
|
, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
src/sys_tray.hpp
Normal file
24
src/sys_tray.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SystemTray {
|
||||||
|
SDL_Window* _main_window {nullptr};
|
||||||
|
SDL_Tray* _tray {nullptr};
|
||||||
|
|
||||||
|
SDL_TrayEntry* _entry_showhide {nullptr};
|
||||||
|
SDL_TrayEntry* _entry_status {nullptr};
|
||||||
|
|
||||||
|
public:
|
||||||
|
SystemTray(SDL_Window* main_window);
|
||||||
|
~SystemTray(void);
|
||||||
|
|
||||||
|
void setIcon(SDL_Surface* surf);
|
||||||
|
void setStatusText(const std::string& status);
|
||||||
|
|
||||||
|
// check if window is visible and adjust text
|
||||||
|
void update(void);
|
||||||
|
};
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user