mirror of https://github.com/procxx/kepka.git
Implement global menu on Linux
This commit is contained in:
parent
5f33abd391
commit
46445e0542
|
@ -2239,6 +2239,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_linux_menu_undo" = "Undo";
|
||||
"lng_linux_menu_redo" = "Redo";
|
||||
"lng_linux_menu_tools" = "Tools";
|
||||
"lng_linux_menu_help" = "Help";
|
||||
|
||||
"lng_linux_no_audio_prefs" = "You don't have any audio configuration applications installed.";
|
||||
|
||||
|
|
|
@ -13,15 +13,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/linux/linux_desktop_environment.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_widget.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "main/main_account.h" // Account::sessionChanges.
|
||||
#include "mainwindow.h"
|
||||
#include "core/application.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/about_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QSize>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
#include <QtDBus/QDBusInterface>
|
||||
|
@ -46,6 +54,10 @@ constexpr auto kSNIWatcherService = "org.kde.StatusNotifierWatcher"_cs;
|
|||
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
|
||||
constexpr auto kTrayIconFilename = "tdesktop-trayicon-XXXXXX.png"_cs;
|
||||
|
||||
constexpr auto kAppMenuService = "com.canonical.AppMenu.Registrar"_cs;
|
||||
constexpr auto kAppMenuObjectPath = "/com/canonical/AppMenu/Registrar"_cs;
|
||||
constexpr auto kAppMenuInterface = kAppMenuService;
|
||||
|
||||
bool TrayIconMuted = true;
|
||||
int32 TrayIconCount = 0;
|
||||
base::flat_map<int, QImage> TrayIconImageBack;
|
||||
|
@ -307,6 +319,71 @@ quint32 djbStringHash(QString string) {
|
|||
return hash;
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
bool AppMenuSupported() {
|
||||
static const auto Available = QDBusInterface(
|
||||
kAppMenuService.utf16(),
|
||||
kAppMenuObjectPath.utf16(),
|
||||
kAppMenuInterface.utf16()).isValid();
|
||||
|
||||
return Available;
|
||||
}
|
||||
|
||||
void RegisterAppMenu(uint winId, const QDBusObjectPath &menuPath) {
|
||||
auto message = QDBusMessage::createMethodCall(
|
||||
kAppMenuService.utf16(),
|
||||
kAppMenuObjectPath.utf16(),
|
||||
kAppMenuInterface.utf16(),
|
||||
qsl("RegisterWindow"));
|
||||
|
||||
message.setArguments({
|
||||
winId,
|
||||
QVariant::fromValue(menuPath)
|
||||
});
|
||||
|
||||
QDBusConnection::sessionBus().send(message);
|
||||
}
|
||||
|
||||
void UnregisterAppMenu(uint winId) {
|
||||
auto message = QDBusMessage::createMethodCall(
|
||||
kAppMenuService.utf16(),
|
||||
kAppMenuObjectPath.utf16(),
|
||||
kAppMenuInterface.utf16(),
|
||||
qsl("UnregisterWindow"));
|
||||
|
||||
message.setArguments({
|
||||
winId
|
||||
});
|
||||
|
||||
QDBusConnection::sessionBus().send(message);
|
||||
}
|
||||
|
||||
void SendKeySequence(
|
||||
Qt::Key key,
|
||||
Qt::KeyboardModifiers modifiers = Qt::NoModifier) {
|
||||
const auto focused = QApplication::focusWidget();
|
||||
if (qobject_cast<QLineEdit*>(focused)
|
||||
|| qobject_cast<QTextEdit*>(focused)
|
||||
|| qobject_cast<HistoryInner*>(focused)) {
|
||||
QApplication::postEvent(
|
||||
focused,
|
||||
new QKeyEvent(QEvent::KeyPress, key, modifiers));
|
||||
|
||||
QApplication::postEvent(
|
||||
focused,
|
||||
new QKeyEvent(QEvent::KeyRelease, key, modifiers));
|
||||
}
|
||||
}
|
||||
|
||||
void ForceDisabled(QAction *action, bool disabled) {
|
||||
if (action->isEnabled()) {
|
||||
if (disabled) action->setDisabled(true);
|
||||
} else if (!disabled) {
|
||||
action->setDisabled(false);
|
||||
}
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
} // namespace
|
||||
|
||||
MainWindow::MainWindow(not_null<Window::Controller*> controller)
|
||||
|
@ -341,6 +418,12 @@ void MainWindow::initHook() {
|
|||
} else {
|
||||
LOG(("Not using Unity launcher counter."));
|
||||
}
|
||||
|
||||
connect(
|
||||
windowHandle(),
|
||||
&QWindow::visibleChanged,
|
||||
this,
|
||||
&MainWindow::onVisibleChanged);
|
||||
}
|
||||
|
||||
bool MainWindow::hasTrayIcon() const {
|
||||
|
@ -575,9 +658,331 @@ void MainWindow::initTrayMenuHook() {
|
|||
_trayIconMenuXEmbed->deleteOnHide(false);
|
||||
}
|
||||
|
||||
void MainWindow::createGlobalMenu() {
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
if (!AppMenuSupported()) return;
|
||||
|
||||
psMainMenu = new QMenu(this);
|
||||
|
||||
auto file = psMainMenu->addMenu(tr::lng_mac_menu_file(tr::now));
|
||||
|
||||
psLogout = file->addAction(
|
||||
tr::lng_mac_menu_logout(tr::now),
|
||||
App::wnd(),
|
||||
SLOT(onLogout()));
|
||||
|
||||
auto quit = file->addAction(
|
||||
tr::lng_mac_menu_quit_telegram(tr::now, lt_telegram, qsl("Telegram")),
|
||||
App::wnd(),
|
||||
SLOT(quitFromTray()),
|
||||
QKeySequence::Quit);
|
||||
|
||||
quit->setMenuRole(QAction::QuitRole);
|
||||
|
||||
auto edit = psMainMenu->addMenu(tr::lng_mac_menu_edit(tr::now));
|
||||
|
||||
psUndo = edit->addAction(
|
||||
tr::lng_linux_menu_undo(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxUndo()),
|
||||
QKeySequence::Undo);
|
||||
|
||||
psRedo = edit->addAction(
|
||||
tr::lng_linux_menu_redo(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxRedo()),
|
||||
QKeySequence::Redo);
|
||||
|
||||
edit->addSeparator();
|
||||
|
||||
psCut = edit->addAction(
|
||||
tr::lng_mac_menu_cut(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxCut()),
|
||||
QKeySequence::Cut);
|
||||
psCopy = edit->addAction(
|
||||
tr::lng_mac_menu_copy(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxCopy()),
|
||||
QKeySequence::Copy);
|
||||
|
||||
psPaste = edit->addAction(
|
||||
tr::lng_mac_menu_paste(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxPaste()),
|
||||
QKeySequence::Paste);
|
||||
|
||||
psDelete = edit->addAction(
|
||||
tr::lng_mac_menu_delete(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxDelete()),
|
||||
QKeySequence(Qt::ControlModifier | Qt::Key_Backspace));
|
||||
|
||||
edit->addSeparator();
|
||||
|
||||
psBold = edit->addAction(
|
||||
tr::lng_menu_formatting_bold(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxBold()),
|
||||
QKeySequence::Bold);
|
||||
|
||||
psItalic = edit->addAction(
|
||||
tr::lng_menu_formatting_italic(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxItalic()),
|
||||
QKeySequence::Italic);
|
||||
|
||||
psUnderline = edit->addAction(
|
||||
tr::lng_menu_formatting_underline(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxUnderline()),
|
||||
QKeySequence::Underline);
|
||||
|
||||
psStrikeOut = edit->addAction(
|
||||
tr::lng_menu_formatting_strike_out(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxStrikeOut()),
|
||||
Ui::kStrikeOutSequence);
|
||||
|
||||
psMonospace = edit->addAction(
|
||||
tr::lng_menu_formatting_monospace(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxMonospace()),
|
||||
Ui::kMonospaceSequence);
|
||||
|
||||
psClearFormat = edit->addAction(
|
||||
tr::lng_menu_formatting_clear(tr::now),
|
||||
this,
|
||||
SLOT(psLinuxClearFormat()),
|
||||
Ui::kClearFormatSequence);
|
||||
|
||||
edit->addSeparator();
|
||||
|
||||
psSelectAll = edit->addAction(
|
||||
tr::lng_mac_menu_select_all(tr::now),
|
||||
this, SLOT(psLinuxSelectAll()),
|
||||
QKeySequence::SelectAll);
|
||||
|
||||
edit->addSeparator();
|
||||
|
||||
auto prefs = edit->addAction(
|
||||
tr::lng_mac_menu_preferences(tr::now),
|
||||
App::wnd(),
|
||||
SLOT(showSettings()),
|
||||
QKeySequence(Qt::ControlModifier | Qt::Key_Comma));
|
||||
|
||||
prefs->setMenuRole(QAction::PreferencesRole);
|
||||
|
||||
auto tools = psMainMenu->addMenu(tr::lng_linux_menu_tools(tr::now));
|
||||
|
||||
psContacts = tools->addAction(
|
||||
tr::lng_mac_menu_contacts(tr::now),
|
||||
crl::guard(this, [=] {
|
||||
if (isHidden()) {
|
||||
App::wnd()->showFromTray();
|
||||
}
|
||||
|
||||
if (!account().sessionExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ui::show(
|
||||
Box<PeerListBox>(std::make_unique<ContactsBoxController>(
|
||||
sessionController()),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_close(), [box] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
box->addLeftButton(tr::lng_profile_add_contact(), [] {
|
||||
App::wnd()->onShowAddContact();
|
||||
});
|
||||
}));
|
||||
}));
|
||||
|
||||
psAddContact = tools->addAction(
|
||||
tr::lng_mac_menu_add_contact(tr::now),
|
||||
App::wnd(),
|
||||
SLOT(onShowAddContact()));
|
||||
|
||||
tools->addSeparator();
|
||||
|
||||
psNewGroup = tools->addAction(
|
||||
tr::lng_mac_menu_new_group(tr::now),
|
||||
App::wnd(),
|
||||
SLOT(onShowNewGroup()));
|
||||
|
||||
psNewChannel = tools->addAction(
|
||||
tr::lng_mac_menu_new_channel(tr::now),
|
||||
App::wnd(),
|
||||
SLOT(onShowNewChannel()));
|
||||
|
||||
auto help = psMainMenu->addMenu(tr::lng_linux_menu_help(tr::now));
|
||||
|
||||
auto about = help->addAction(
|
||||
tr::lng_mac_menu_about_telegram(
|
||||
tr::now,
|
||||
lt_telegram,
|
||||
qsl("Telegram")),
|
||||
[] {
|
||||
if (App::wnd() && App::wnd()->isHidden()) {
|
||||
App::wnd()->showFromTray();
|
||||
}
|
||||
|
||||
Ui::show(Box<AboutBox>());
|
||||
});
|
||||
|
||||
about->setMenuRole(QAction::AboutQtRole);
|
||||
|
||||
_mainMenuPath.setPath(qsl("/MenuBar"));
|
||||
|
||||
_mainMenuExporter = new DBusMenuExporter(
|
||||
_mainMenuPath.path(),
|
||||
psMainMenu);
|
||||
|
||||
RegisterAppMenu(winId(), _mainMenuPath);
|
||||
|
||||
updateGlobalMenu();
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxUndo() {
|
||||
SendKeySequence(Qt::Key_Z, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxRedo() {
|
||||
SendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxCut() {
|
||||
SendKeySequence(Qt::Key_X, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxCopy() {
|
||||
SendKeySequence(Qt::Key_C, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxPaste() {
|
||||
SendKeySequence(Qt::Key_V, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxDelete() {
|
||||
SendKeySequence(Qt::Key_Delete);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxSelectAll() {
|
||||
SendKeySequence(Qt::Key_A, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxBold() {
|
||||
SendKeySequence(Qt::Key_B, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxItalic() {
|
||||
SendKeySequence(Qt::Key_I, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxUnderline() {
|
||||
SendKeySequence(Qt::Key_U, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxStrikeOut() {
|
||||
SendKeySequence(Qt::Key_X, Qt::ControlModifier | Qt::ShiftModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxMonospace() {
|
||||
SendKeySequence(Qt::Key_M, Qt::ControlModifier | Qt::ShiftModifier);
|
||||
}
|
||||
|
||||
void MainWindow::psLinuxClearFormat() {
|
||||
SendKeySequence(Qt::Key_N, Qt::ControlModifier | Qt::ShiftModifier);
|
||||
}
|
||||
|
||||
void MainWindow::updateGlobalMenuHook() {
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
if (!AppMenuSupported() || !App::wnd() || !positionInited()) return;
|
||||
|
||||
const auto focused = QApplication::focusWidget();
|
||||
auto canUndo = false;
|
||||
auto canRedo = false;
|
||||
auto canCut = false;
|
||||
auto canCopy = false;
|
||||
auto canPaste = false;
|
||||
auto canDelete = false;
|
||||
auto canSelectAll = false;
|
||||
const auto clipboardHasText = QGuiApplication::clipboard()
|
||||
->ownsClipboard();
|
||||
auto markdownEnabled = false;
|
||||
if (const auto edit = qobject_cast<QLineEdit*>(focused)) {
|
||||
canCut = canCopy = canDelete = edit->hasSelectedText();
|
||||
canSelectAll = !edit->text().isEmpty();
|
||||
canUndo = edit->isUndoAvailable();
|
||||
canRedo = edit->isRedoAvailable();
|
||||
canPaste = clipboardHasText;
|
||||
} else if (const auto edit = qobject_cast<QTextEdit*>(focused)) {
|
||||
canCut = canCopy = canDelete = edit->textCursor().hasSelection();
|
||||
canSelectAll = !edit->document()->isEmpty();
|
||||
canUndo = edit->document()->isUndoAvailable();
|
||||
canRedo = edit->document()->isRedoAvailable();
|
||||
canPaste = clipboardHasText;
|
||||
if (canCopy) {
|
||||
if (const auto inputField = qobject_cast<Ui::InputField*>(
|
||||
focused->parentWidget())) {
|
||||
markdownEnabled = inputField->isMarkdownEnabled();
|
||||
}
|
||||
}
|
||||
} else if (const auto list = qobject_cast<HistoryInner*>(focused)) {
|
||||
canCopy = list->canCopySelected();
|
||||
canDelete = list->canDeleteSelected();
|
||||
}
|
||||
App::wnd()->updateIsActive(0);
|
||||
const auto logged = account().sessionExists();
|
||||
const auto locked = Core::App().locked();
|
||||
const auto inactive = !logged || locked;
|
||||
const auto support = logged && account().session().supportMode();
|
||||
ForceDisabled(psLogout, !logged && !locked);
|
||||
ForceDisabled(psUndo, !canUndo);
|
||||
ForceDisabled(psRedo, !canRedo);
|
||||
ForceDisabled(psCut, !canCut);
|
||||
ForceDisabled(psCopy, !canCopy);
|
||||
ForceDisabled(psPaste, !canPaste);
|
||||
ForceDisabled(psDelete, !canDelete);
|
||||
ForceDisabled(psSelectAll, !canSelectAll);
|
||||
ForceDisabled(psContacts, inactive || support);
|
||||
ForceDisabled(psAddContact, inactive);
|
||||
ForceDisabled(psNewGroup, inactive || support);
|
||||
ForceDisabled(psNewChannel, inactive || support);
|
||||
|
||||
ForceDisabled(psBold, !markdownEnabled);
|
||||
ForceDisabled(psItalic, !markdownEnabled);
|
||||
ForceDisabled(psUnderline, !markdownEnabled);
|
||||
ForceDisabled(psStrikeOut, !markdownEnabled);
|
||||
ForceDisabled(psMonospace, !markdownEnabled);
|
||||
ForceDisabled(psClearFormat, !markdownEnabled);
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
void MainWindow::onVisibleChanged(bool visible) {
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
if (AppMenuSupported() && !_mainMenuPath.path().isEmpty()) {
|
||||
if (visible) {
|
||||
RegisterAppMenu(winId(), _mainMenuPath);
|
||||
} else {
|
||||
UnregisterAppMenu(winId());
|
||||
}
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
delete _sniTrayIcon;
|
||||
|
||||
if (AppMenuSupported()) {
|
||||
UnregisterAppMenu(winId());
|
||||
delete _mainMenuExporter;
|
||||
delete psMainMenu;
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
delete _trayIconMenuXEmbed;
|
||||
|
|
|
@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
#include "statusnotifieritem.h"
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtDBus/QDBusObjectPath>
|
||||
#include <dbusmenuexporter.h>
|
||||
#endif
|
||||
|
||||
namespace Platform {
|
||||
|
@ -45,14 +47,33 @@ public slots:
|
|||
const QString &newOwner);
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
void psLinuxUndo();
|
||||
void psLinuxRedo();
|
||||
void psLinuxCut();
|
||||
void psLinuxCopy();
|
||||
void psLinuxPaste();
|
||||
void psLinuxDelete();
|
||||
void psLinuxSelectAll();
|
||||
|
||||
void psLinuxBold();
|
||||
void psLinuxItalic();
|
||||
void psLinuxUnderline();
|
||||
void psLinuxStrikeOut();
|
||||
void psLinuxMonospace();
|
||||
void psLinuxClearFormat();
|
||||
|
||||
void onVisibleChanged(bool visible);
|
||||
|
||||
protected:
|
||||
void initHook() override;
|
||||
void unreadCounterChangedHook() override;
|
||||
void updateGlobalMenuHook() override;
|
||||
|
||||
void initTrayMenuHook() override;
|
||||
bool hasTrayIcon() const override;
|
||||
|
||||
void workmodeUpdated(DBIWorkMode mode) override;
|
||||
void createGlobalMenu() override;
|
||||
|
||||
QSystemTrayIcon *trayIcon = nullptr;
|
||||
QMenu *trayIconMenu = nullptr;
|
||||
|
@ -77,6 +98,30 @@ private:
|
|||
StatusNotifierItem *_sniTrayIcon = nullptr;
|
||||
std::unique_ptr<QTemporaryFile> _trayIconFile = nullptr;
|
||||
|
||||
DBusMenuExporter *_mainMenuExporter = nullptr;
|
||||
QDBusObjectPath _mainMenuPath;
|
||||
|
||||
QMenu *psMainMenu = nullptr;
|
||||
QAction *psLogout = nullptr;
|
||||
QAction *psUndo = nullptr;
|
||||
QAction *psRedo = nullptr;
|
||||
QAction *psCut = nullptr;
|
||||
QAction *psCopy = nullptr;
|
||||
QAction *psPaste = nullptr;
|
||||
QAction *psDelete = nullptr;
|
||||
QAction *psSelectAll = nullptr;
|
||||
QAction *psContacts = nullptr;
|
||||
QAction *psAddContact = nullptr;
|
||||
QAction *psNewGroup = nullptr;
|
||||
QAction *psNewChannel = nullptr;
|
||||
|
||||
QAction *psBold = nullptr;
|
||||
QAction *psItalic = nullptr;
|
||||
QAction *psUnderline = nullptr;
|
||||
QAction *psStrikeOut = nullptr;
|
||||
QAction *psMonospace = nullptr;
|
||||
QAction *psClearFormat = nullptr;
|
||||
|
||||
void setSNITrayIcon(int counter, bool muted, bool firstShow = false);
|
||||
void attachToSNITrayIcon();
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
|
|
Loading…
Reference in New Issue