Adding a tray icon.
This commit is contained in:
@@ -43,6 +43,9 @@ add_executable(rktwebview_prg
|
||||
rkt_protocol.h
|
||||
rktwebview_types.h
|
||||
utils.cpp utils.h
|
||||
|
||||
build_menu_from_json.cpp
|
||||
build_menu_from_json.h
|
||||
)
|
||||
|
||||
add_executable(rktwebview_test
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonObject>
|
||||
#include <QMenu>
|
||||
#include "rktwebview_qt.h"
|
||||
|
||||
static QAction *addMenuItemFromJson(Rktwebview_qt *owner,
|
||||
QMenu *menu,
|
||||
const QJsonObject &item,
|
||||
int source_handle);
|
||||
|
||||
static QMenu *buildMenuObjectFromJson(Rktwebview_qt *owner,
|
||||
const QJsonArray &items,
|
||||
int source_handle,
|
||||
QWidget *parent = nullptr)
|
||||
{
|
||||
QMenu *menu = new QMenu(parent);
|
||||
|
||||
for (const QJsonValue &v : items) {
|
||||
if (!v.isObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject item = v.toObject();
|
||||
|
||||
if (item.value("separator").toBool(false)) {
|
||||
menu->addSeparator();
|
||||
continue;
|
||||
}
|
||||
|
||||
addMenuItemFromJson(owner, menu, item, source_handle);
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
static QAction *addMenuItemFromJson(Rktwebview_qt *owner,
|
||||
QMenu *menu,
|
||||
const QJsonObject &item,
|
||||
int source_handle)
|
||||
{
|
||||
QString id = item.value("id").toString();
|
||||
QString name = item.value("name").toString();
|
||||
QString icon_file = item.value("icon").toString();
|
||||
|
||||
QAction *action = nullptr;
|
||||
|
||||
QJsonValue submenu_value = item.value("submenu");
|
||||
if (submenu_value.isObject()) {
|
||||
QJsonObject submenu_obj = submenu_value.toObject();
|
||||
QJsonArray submenu_items = submenu_obj.value("menu").toArray();
|
||||
|
||||
QMenu *submenu = buildMenuObjectFromJson(owner,
|
||||
submenu_items,
|
||||
source_handle,
|
||||
menu);
|
||||
|
||||
if (!icon_file.isEmpty()) {
|
||||
submenu->setIcon(QIcon(icon_file));
|
||||
}
|
||||
|
||||
submenu->setTitle(name);
|
||||
action = menu->addMenu(submenu);
|
||||
} else {
|
||||
if (icon_file.isEmpty()) {
|
||||
action = menu->addAction(name);
|
||||
} else {
|
||||
action = menu->addAction(QIcon(icon_file), name);
|
||||
}
|
||||
|
||||
QObject::connect(action, &QAction::triggered,
|
||||
owner, [owner, source_handle, id]() {
|
||||
EventContainer e("tray-menu-item-chosen");
|
||||
e["id"] = id;
|
||||
e["menu_item"] = id;
|
||||
owner->triggerEvent(source_handle, e);
|
||||
});
|
||||
}
|
||||
|
||||
action->setData(id);
|
||||
return action;
|
||||
}
|
||||
|
||||
QMenu *buildMenuFromJson(Rktwebview_qt *self, const QString &menu_json, int source_handle)
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(menu_json.toUtf8(), &err);
|
||||
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QJsonObject root = doc.object();
|
||||
QJsonArray items = root.value("menu").toArray();
|
||||
|
||||
return buildMenuObjectFromJson(self, items, source_handle);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef BUILD_MENU_FROM_JSON_H
|
||||
#define BUILD_MENU_FROM_JSON_H
|
||||
|
||||
class QMenu;
|
||||
class Rktwebview_qt;
|
||||
class QString;
|
||||
|
||||
QMenu *buildMenuFromJson(Rktwebview_qt *self, const QString &menu_json, int source_handle);
|
||||
|
||||
#endif // BUILD_MENU_FROM_JSON_H
|
||||
@@ -29,6 +29,11 @@
|
||||
#define COMMAND_NEW_CONTEXT 23
|
||||
#define COMMAND_MESSAGE 24
|
||||
#define COMMAND_SET_ICON 25
|
||||
#define COMMAND_CREATE_TRAY 26
|
||||
#define COMMAND_TRAY_SET_ICON 27
|
||||
#define COMMAND_TRAY_SET_TOOLTIP 28
|
||||
#define COMMAND_TRAY_SHOW_MESSAGE 29
|
||||
#define COMMAND_TRAY_SET_MENU 30
|
||||
|
||||
class Command
|
||||
{
|
||||
|
||||
@@ -339,6 +339,54 @@ void Handler::run()
|
||||
result_queue->enqueue(r);
|
||||
}
|
||||
break;
|
||||
case CMD_CREATE_TRAY: {
|
||||
QString icon_file = data_obj["icon"].toString();
|
||||
QString tooltip = data_obj["tooltip"].toString();
|
||||
|
||||
int tray = webview_handler->rktTrayCreate(icon_file.toUtf8().constData(),
|
||||
tooltip.toUtf8().constData(),
|
||||
event_cb);
|
||||
result_queue->enqueue(tray);
|
||||
}
|
||||
break;
|
||||
case CMD_TRAY_SET_ICON: {
|
||||
int tray = data_obj["tray"].toInt();
|
||||
QString icon_file = data_obj["icon"].toString();
|
||||
|
||||
result_t r = webview_handler->rktTraySetIcon(tray,
|
||||
icon_file.toUtf8().constData());
|
||||
result_queue->enqueue(r);
|
||||
}
|
||||
break;
|
||||
case CMD_TRAY_SET_TOOLTIP: {
|
||||
int tray = data_obj["tray"].toInt();
|
||||
QString tooltip = data_obj["tooltip"].toString();
|
||||
|
||||
result_t r = webview_handler->rktTraySetTooltip(tray,
|
||||
tooltip.toUtf8().constData());
|
||||
result_queue->enqueue(r);
|
||||
}
|
||||
break;
|
||||
case CMD_TRAY_SHOW_MESSAGE: {
|
||||
int tray = data_obj["tray"].toInt();
|
||||
QString title = data_obj["title"].toString();
|
||||
QString message = data_obj["message"].toString();
|
||||
|
||||
result_t r = webview_handler->rktTrayShowMessage(tray,
|
||||
title.toUtf8().constData(),
|
||||
message.toUtf8().constData());
|
||||
result_queue->enqueue(r);
|
||||
}
|
||||
break;
|
||||
case CMD_TRAY_SET_MENU: {
|
||||
int tray = data_obj["tray"].toInt();
|
||||
QString menu_json = data_obj["menu_json"].toString();
|
||||
|
||||
result_t r = webview_handler->rktTraySetMenu(tray,
|
||||
menu_json.toUtf8().constData());
|
||||
result_queue->enqueue(r);
|
||||
}
|
||||
break;
|
||||
case CMD_CHOOSE_DIR: {
|
||||
int wv = data_obj["wv"].toInt();
|
||||
QString title = data_obj["title"].toString();
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
#define CMD_SET_LOGLEVEL 26 // arguments: ll: int
|
||||
#define CMD_INFO 27 // arguments: none
|
||||
#define CMD_SET_ICON 28 // arguments: wv: int, icon:string (absolute filename to icon in png/jpg/svg)
|
||||
#define CMD_CREATE_TRAY 29 // arguments: icon: string (path to icon file), tooltip: string -> tray: int
|
||||
#define CMD_TRAY_SET_ICON 30 // arguments: tray: int, icon: string -> result_t: int
|
||||
#define CMD_TRAY_SET_TOOLTIP 31 // arguments: tray: int, tooltip: string -> result_t: int
|
||||
#define CMD_TRAY_SHOW_MESSAGE 32 // arguments: tray: int, title: string, message: string -> result_t: int
|
||||
#define CMD_TRAY_SET_MENU 33 // arguments: tray: int, menu_json: string -> result_t: int
|
||||
|
||||
|
||||
#define CMD_NOOP 33621
|
||||
#define RESULT_QUIT 36379
|
||||
|
||||
@@ -876,3 +876,51 @@ void rkt_webview_exit_done(int done)
|
||||
ERROR0("rkt_webview_exit_done called with 'false', i.e. this library did not have a cleanup call\n");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Tray specific functions
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
rktwebview_t rkt_webview_tray_create(const char *icon_file,
|
||||
const char *tooltip)
|
||||
{
|
||||
RKT_WEBVIEW_INIT;
|
||||
FAIL_WV
|
||||
|
||||
JSON j;
|
||||
j["icon"] = icon_file;
|
||||
j["tooltip"] = tooltip;
|
||||
|
||||
handler->command_queue->enqueue(CMD_CREATE_TRAY, j.dump());
|
||||
|
||||
int result;
|
||||
std::string json_result;
|
||||
while (!handler->command_result_queue->dequeue(result, json_result, MAX_WAIT_RESULT)) {
|
||||
if (handler->alive_error) {
|
||||
return result_t::failed;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result_t rkt_webview_tray_set_icon(rktwebview_t tray, const char *icon_file)
|
||||
{
|
||||
CMDRES(CMD_TRAY_SET_ICON, tray, "icon", icon_file)
|
||||
}
|
||||
|
||||
result_t rkt_webview_tray_set_tooltip(rktwebview_t tray, const char *tooltip)
|
||||
{
|
||||
CMDRES(CMD_TRAY_SET_TOOLTIP, tray, "tooltip", tooltip)
|
||||
}
|
||||
|
||||
result_t rkt_webview_tray_show_message(rktwebview_t tray, const char *title, const char *message)
|
||||
{
|
||||
CMDRES2(CMD_TRAY_SHOW_MESSAGE, tray, "title", title, "message", message)
|
||||
}
|
||||
|
||||
result_t rkt_webview_tray_set_menu(rktwebview_t tray, const char *menu_json)
|
||||
{
|
||||
CMDRES(CMD_TRAY_SET_MENU, tray, "menu", menu_json)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,13 @@ RKTWEBVIEW_EXPORT result_t rkt_webview_file_save(rktwebview_t w, const char *tit
|
||||
|
||||
RKTWEBVIEW_EXPORT result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type);
|
||||
|
||||
// Tray Icon functions (note, also hide/show/close can be used).
|
||||
RKTWEBVIEW_EXPORT rktwebview_t rkt_webview_tray_create(const char *icon_file, const char *tooltip);
|
||||
RKTWEBVIEW_EXPORT result_t rkt_webview_tray_set_icon(rktwebview_t tray, const char *icon_file);
|
||||
RKTWEBVIEW_EXPORT result_t rkt_webview_tray_set_tooltip(rktwebview_t tray, const char *tooltip);
|
||||
RKTWEBVIEW_EXPORT result_t rkt_webview_tray_show_message(rktwebview_t tray, const char *title, const char *message);
|
||||
RKTWEBVIEW_EXPORT result_t rkt_webview_tray_set_menu(rktwebview_t tray, const char *menu_json);
|
||||
|
||||
}
|
||||
|
||||
#endif // RKTWEBVIEW_H
|
||||
|
||||
+227
-1
@@ -18,15 +18,36 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QAbstractButton>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include "build_menu_from_json.h"
|
||||
|
||||
static inline char *copyString(const char *s)
|
||||
{
|
||||
int l = strlen(s);
|
||||
int l = static_cast<int>(strlen(s));
|
||||
char *cpy = static_cast<char *>(malloc(l + 1));
|
||||
memcpy(cpy, s, l + 1);
|
||||
return cpy;
|
||||
}
|
||||
|
||||
static QString activationReasonToString(QSystemTrayIcon::ActivationReason r)
|
||||
{
|
||||
switch (r) {
|
||||
case QSystemTrayIcon::Context:
|
||||
return "context-menu"; // rechtsklik / menu openen
|
||||
case QSystemTrayIcon::DoubleClick:
|
||||
return "double-click";
|
||||
case QSystemTrayIcon::Trigger:
|
||||
return "click"; // meestal linkerklik
|
||||
case QSystemTrayIcon::MiddleClick:
|
||||
return "middle-click";
|
||||
case QSystemTrayIcon::Unknown:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void Rktwebview_qt::processCommand(Command *cmd)
|
||||
{
|
||||
switch(cmd->cmd) {
|
||||
@@ -168,6 +189,19 @@ void Rktwebview_qt::processCommand(Command *cmd)
|
||||
}
|
||||
_view_js_callbacks.remove(wv);
|
||||
delete w;
|
||||
} else if (_trays.contains(wv)) {
|
||||
QSystemTrayIcon *tray = _trays[wv];
|
||||
_trays.remove(wv);
|
||||
|
||||
if (_tray_menus.contains(wv)) {
|
||||
QMenu *menu = _tray_menus[wv];
|
||||
_tray_menus.remove(wv);
|
||||
delete menu;
|
||||
}
|
||||
|
||||
tray->hide();
|
||||
delete tray;
|
||||
cmd->result = true;
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
@@ -306,6 +340,18 @@ void Rktwebview_qt::processCommand(Command *cmd)
|
||||
w->activateWindow();
|
||||
}
|
||||
cmd->result = true;
|
||||
} else if (_trays.contains(wv)) {
|
||||
QSystemTrayIcon *tray = _trays[wv];
|
||||
|
||||
if (cmd->cmd == COMMAND_SHOW_WIN) {
|
||||
tray->show();
|
||||
cmd->result = true;
|
||||
} else if (cmd->cmd == COMMAND_HIDE_WIN) {
|
||||
tray->hide();
|
||||
cmd->result = true;
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
@@ -449,6 +495,108 @@ void Rktwebview_qt::processCommand(Command *cmd)
|
||||
}
|
||||
cmd->done = true;
|
||||
}
|
||||
case COMMAND_CREATE_TRAY: {
|
||||
QString icon_file = cmd->args[0].toString();
|
||||
QString tooltip = cmd->args[1].toString();
|
||||
|
||||
int id = nextHandle();
|
||||
|
||||
QSystemTrayIcon *tray = new QSystemTrayIcon(this);
|
||||
tray->setIcon(QIcon(icon_file));
|
||||
tray->setToolTip(tooltip);
|
||||
|
||||
connect(tray, &QSystemTrayIcon::activated,
|
||||
this, [this, id](QSystemTrayIcon::ActivationReason reason) {
|
||||
EventContainer e("tray-activated");
|
||||
e["reason"] = activationReasonToString(reason);
|
||||
this->triggerEvent(id, e);
|
||||
});
|
||||
|
||||
connect(tray, &QSystemTrayIcon::messageClicked,
|
||||
this, [this, id]() {
|
||||
EventContainer e("tray-message-clicked");
|
||||
this->triggerEvent(id, e);
|
||||
});
|
||||
|
||||
_trays[id] = tray;
|
||||
|
||||
tray->show();
|
||||
|
||||
cmd->result = id;
|
||||
cmd->done = true;
|
||||
}
|
||||
break;
|
||||
case COMMAND_TRAY_SET_ICON: {
|
||||
int tray_id = cmd->args[0].toInt();
|
||||
QString icon_file = cmd->args[1].toString();
|
||||
|
||||
if (_trays.contains(tray_id)) {
|
||||
_trays[tray_id]->setIcon(QIcon(icon_file));
|
||||
cmd->result = true;
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
|
||||
cmd->done = true;
|
||||
}
|
||||
break;
|
||||
case COMMAND_TRAY_SET_TOOLTIP: {
|
||||
int tray_id = cmd->args[0].toInt();
|
||||
QString tooltip = cmd->args[1].toString();
|
||||
|
||||
if (_trays.contains(tray_id)) {
|
||||
_trays[tray_id]->setToolTip(tooltip);
|
||||
cmd->result = true;
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
|
||||
cmd->done = true;
|
||||
}
|
||||
break;
|
||||
case COMMAND_TRAY_SHOW_MESSAGE: {
|
||||
int tray_id = cmd->args[0].toInt();
|
||||
QString title = cmd->args[1].toString();
|
||||
QString message = cmd->args[2].toString();
|
||||
|
||||
if (_trays.contains(tray_id)) {
|
||||
_trays[tray_id]->showMessage(title, message);
|
||||
cmd->result = true;
|
||||
} else {
|
||||
cmd->result = false;
|
||||
}
|
||||
|
||||
cmd->done = true;
|
||||
}
|
||||
break;
|
||||
case COMMAND_TRAY_SET_MENU: {
|
||||
int tray_id = cmd->args[0].toInt();
|
||||
QString menu_json = cmd->args[1].toString();
|
||||
|
||||
if (!_trays.contains(tray_id)) {
|
||||
cmd->result = false;
|
||||
cmd->done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_tray_menus.contains(tray_id)) {
|
||||
QMenu *old = _tray_menus[tray_id];
|
||||
_tray_menus.remove(tray_id);
|
||||
delete old;
|
||||
}
|
||||
|
||||
QMenu *menu = buildMenuFromJson(this, menu_json, tray_id);
|
||||
if (menu == nullptr) {
|
||||
cmd->result = false;
|
||||
cmd->done = true;
|
||||
} else {
|
||||
_tray_menus[tray_id] = menu;
|
||||
_trays[tray_id]->setContextMenu(menu);
|
||||
}
|
||||
|
||||
cmd->result = true;
|
||||
cmd->done = true;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
cmd->result = false;
|
||||
@@ -888,6 +1036,71 @@ result_t Rktwebview_qt::rktMessageBox(rktwebview_t w, const char *title, const c
|
||||
return r ? result_t::oke : result_t::failed;
|
||||
}
|
||||
|
||||
rktwebview_t Rktwebview_qt::rktTrayCreate(const char *icon_file, const char *tooltip, event_cb_t evt_cb)
|
||||
{
|
||||
Command c(COMMAND_CREATE_TRAY);
|
||||
c.args.push_back(icon_file);
|
||||
c.args.push_back(tooltip);
|
||||
|
||||
void *function = reinterpret_cast<void *>(evt_cb);
|
||||
QVariant f(QVariant::fromValue(function));
|
||||
c.args.push_back(f);
|
||||
|
||||
postCommand(&c);
|
||||
while(!c.done) { doEvents(); }
|
||||
|
||||
return c.result.toInt();
|
||||
}
|
||||
|
||||
result_t Rktwebview_qt::rktTraySetIcon(rktwebview_t tray, const char *icon_file)
|
||||
{
|
||||
Command c(COMMAND_TRAY_SET_ICON);
|
||||
c.args.push_back(tray);
|
||||
c.args.push_back(icon_file);
|
||||
|
||||
postCommand(&c);
|
||||
while(!c.done) { doEvents(); }
|
||||
|
||||
return c.result.toBool() ? result_t::oke : result_t::failed;
|
||||
}
|
||||
|
||||
result_t Rktwebview_qt::rktTraySetTooltip(rktwebview_t tray, const char *tooltip)
|
||||
{
|
||||
Command c(COMMAND_TRAY_SET_TOOLTIP);
|
||||
c.args.push_back(tray);
|
||||
c.args.push_back(tooltip);
|
||||
|
||||
postCommand(&c);
|
||||
while(!c.done) { doEvents(); }
|
||||
|
||||
return c.result.toBool() ? result_t::oke : result_t::failed;
|
||||
}
|
||||
|
||||
result_t Rktwebview_qt::rktTrayShowMessage(rktwebview_t tray, const char *title, const char *message)
|
||||
{
|
||||
Command c(COMMAND_TRAY_SHOW_MESSAGE);
|
||||
c.args.push_back(tray);
|
||||
c.args.push_back(title);
|
||||
c.args.push_back(message);
|
||||
|
||||
postCommand(&c);
|
||||
while(!c.done) { doEvents(); }
|
||||
|
||||
return c.result.toBool() ? result_t::oke : result_t::failed;
|
||||
}
|
||||
|
||||
result_t Rktwebview_qt::rktTraySetMenu(rktwebview_t tray, const char *menu_json)
|
||||
{
|
||||
Command c(COMMAND_TRAY_SET_MENU);
|
||||
c.args.push_back(tray);
|
||||
c.args.push_back(menu_json);
|
||||
|
||||
postCommand(&c);
|
||||
while(!c.done) { doEvents(); }
|
||||
|
||||
return c.result.toBool() ? result_t::oke : result_t::failed;
|
||||
}
|
||||
|
||||
result_t Rktwebview_qt::doWindow(rktwebview_t w, int cmd, result_t on_error)
|
||||
{
|
||||
Command c(cmd);
|
||||
@@ -1085,5 +1298,18 @@ Rktwebview_qt::~Rktwebview_qt()
|
||||
delete p;
|
||||
}
|
||||
|
||||
QList<int> tray_keys = _trays.keys();
|
||||
for(i = 0, N = tray_keys.size(); i < N; i++) {
|
||||
QSystemTrayIcon *tray = _trays[tray_keys[i]];
|
||||
tray->hide();
|
||||
delete tray;
|
||||
}
|
||||
|
||||
QList<int> menu_keys = _tray_menus.keys();
|
||||
for(i = 0, N = menu_keys.size(); i < N; i++) {
|
||||
QMenu *menu = _tray_menus[menu_keys[i]];
|
||||
delete menu;
|
||||
}
|
||||
|
||||
delete _app;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ class WebViewQt;
|
||||
class WebviewWindow;
|
||||
class Command;
|
||||
class CommandEvent;
|
||||
class QSystemTrayIcon;
|
||||
class QMenu;
|
||||
|
||||
class Rktwebview_qt : public QObject
|
||||
{
|
||||
@@ -30,6 +32,8 @@ private:
|
||||
int _context_counter;
|
||||
QHash<int, QWebEngineProfile *> _contexts;
|
||||
QHash<int, WebviewWindow *> _views;
|
||||
QHash<int, QSystemTrayIcon *> _trays;
|
||||
QHash<int, QMenu *> _tray_menus;
|
||||
QHash<int, event_cb_t> _view_js_callbacks;
|
||||
|
||||
QTimer _process_events;
|
||||
@@ -114,6 +118,13 @@ public:
|
||||
|
||||
bool rktValid(rktwebview_t wv);
|
||||
|
||||
public: // Tray
|
||||
rktwebview_t rktTrayCreate(const char *icon_file, const char *tooltip, event_cb_t evt_cb);
|
||||
result_t rktTraySetIcon(rktwebview_t tray, const char *icon_file);
|
||||
result_t rktTraySetTooltip(rktwebview_t tray, const char *tooltip);
|
||||
result_t rktTrayShowMessage(rktwebview_t tray, const char *title, const char *message);
|
||||
result_t rktTraySetMenu(rktwebview_t tray, const char *menu_json);
|
||||
|
||||
public:
|
||||
// Events for the backend
|
||||
void pageLoaded(rktwebview_t w, bool ok);
|
||||
|
||||
Reference in New Issue
Block a user