Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LOL #9

Merged
merged 5 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
options: --privileged
strategy:
matrix:
image: ['ubuntu:23.04', 'ubuntu:22.10', 'ubuntu:22.04', 'ubuntu:20.04', 'debian:12', 'debian:11', 'debian:10', 'opensuse/leap:15.4', 'fedora:38', 'fedora:37']
image: ['ubuntu:23.04', 'ubuntu:22.04', 'ubuntu:20.04', 'debian:12', 'debian:11', 'debian:10', 'opensuse/leap:15.4', 'fedora:40', 'fedora:39', 'fedora:38']
fail-fast: false

steps:
Expand Down
4 changes: 2 additions & 2 deletions .scripts/ibus-remove.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/sh

rm -f /usr/share/ibus/component/im-emoji-picker-ibus.xml
rm -f /usr/share/ibus/component/ibusimemojipicker.xml

rm -f /usr/lib/ibus/im-emoji-picker-ibus
rm -f /usr/lib/ibus/ibusimemojipicker

rm -f /usr/share/icons/hicolor/32x32/apps/im-emoji-picker.png
touch /usr/share/icons/hicolor
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ useSystemQtTheme=false
; (requires IMF restart)
windowOpacity=0.9

; Use something like the following to add custom hotkeys (target = the default key press as seen below):
; [customHotKeys]
; 1\sourceKeyChr=#
; 1\targetKeySeq=shift+tab
; size=1
[customHotKeys]
size=0

; The files to load emoji aliases from.
; Refer to src/res/aliases/github-emojis.ini for an example
; (requires IMF restart)
Expand Down Expand Up @@ -194,6 +202,7 @@ size=1
- `return` = write emoji to target input
- `shift+return` = write emoji to target input and close emoji picker
- `tab` = change view (MRU, List, Kaomoji)
- `shift+tab` = change view (MRU, List, Kaomoji) (reverse)
- `f4` = open settings file and close emoji picker

## Building 🤓
Expand Down
37 changes: 37 additions & 0 deletions src/EmojiPickerSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ void EmojiPickerSettings::writeDefaultsToDisk() {
s.useSystemEmojiFontWidthHeuristics(s.useSystemEmojiFontWidthHeuristics());
s.scaleFactor(s.scaleFactor());
s.saveKaomojiInMRU(s.saveKaomojiInMRU());
s.customHotKeys(s.customHotKeys());
}

EmojiPickerSettings::EmojiPickerSettings() : QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName(), nullptr) {
Expand Down Expand Up @@ -199,6 +200,42 @@ void EmojiPickerSettings::saveKaomojiInMRU(bool saveKaomojiInMRU) {
setValue("saveKaomojiInMRU", saveKaomojiInMRU);
}

std::unordered_map<char, QKeySequence> EmojiPickerSettings::customHotKeys() {
std::unordered_map<char, QKeySequence> result;

int len = beginReadArray("customHotKeys");
for (int i = 0; i < len; i++) {
setArrayIndex(i);

auto keyValue = value("sourceKeyChr");
char key = 0;
if (keyValue.type() == QVariant::StringList) {
key = ',';
} else {
key = keyValue.toString().at(0).toLatin1();
}
auto target = QKeySequence{value("targetKeySeq").toString(), QKeySequence::PortableText};
result.emplace(key, target);
}
endArray();

return result;
}
void EmojiPickerSettings::customHotKeys(const std::unordered_map<char, QKeySequence>& customHotKeys) {
beginWriteArray("customHotKeys", customHotKeys.size());
int i = 0;
for (const auto& [key, target] : customHotKeys) {
setArrayIndex(i++);
if (key == ',') {
setValue("sourceKeyChr", QStringList{"", ""});
} else {
setValue("sourceKeyChr", QString::fromStdString(std::string{key}));
}
setValue("targetKeySeq", target.toString(QKeySequence::PortableText));
}
endArray();
}

EmojiPickerCache::EmojiPickerCache() : QSettings(path(), QSettings::IniFormat) {
}

Expand Down
4 changes: 4 additions & 0 deletions src/EmojiPickerSettings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "emojis.hpp"
#include <QFontMetrics>
#include <QSettings>
#include <unordered_map>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -53,6 +54,9 @@ class EmojiPickerSettings : public QSettings {

bool saveKaomojiInMRU() const;
void saveKaomojiInMRU(bool saveKaomojiInMRU);

std::unordered_map<char, QKeySequence> customHotKeys();
void customHotKeys(const std::unordered_map<char, QKeySequence>& customHotKeys);
};

class EmojiPickerCache : public QSettings {
Expand Down
105 changes: 88 additions & 17 deletions src/EmojiPickerWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <vector>
#include <QTextStream>
#include <QFile>
#include <unistd.h>
#include <mutex>
#include <condition_variable>

Emoji convertKaomojiToEmoji(const Kaomoji& kaomoji) {
return Emoji{kaomoji.name, kaomoji.text, -1};
Expand All @@ -32,9 +35,9 @@ void moveQWidgetToCenter(QWidget* window) {
}

QPoint createPointInScreen(QWidget* window, QRect newPoint) {
QPoint result{newPoint.x(), newPoint.y()};
result.setX(result.x() + newPoint.width());
result.setY(result.y() + newPoint.height());
QPoint result{0, 0};
result.setX(newPoint.x() + newPoint.width());
result.setY(newPoint.y() + newPoint.height());

QRect windowRect = window->geometry();

Expand All @@ -43,7 +46,6 @@ QPoint createPointInScreen(QWidget* window, QRect newPoint) {
if (!screen) {
return result;
}

QRect screenRect = screen->geometry();
#else
QRect screenRect = QApplication::desktop()->availableGeometry(result);
Expand Down Expand Up @@ -78,17 +80,19 @@ std::function<void()> resetInputMethodEngine = []() {
ThreadsafeQueue<std::shared_ptr<EmojiCommand>> emojiCommandQueue;

EmojiPickerWindow::EmojiPickerWindow() : QMainWindow() {
setFocusPolicy(Qt::NoFocus);
setAttribute(Qt::WA_ShowWithoutActivating);
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus);
setWindowIcon(QIcon(":/res/im-emoji-picker_72x72.png"));
setWindowOpacity(_settings.windowOpacity());
setFocusPolicy(Qt::NoFocus);
setAttribute(Qt::WA_ShowWithoutActivating);
setWindowTitle("im-emoji-picker");
setFixedSize(340, 190);

_searchContainerWidget->setLayout(_searchContainerLayout);
_searchContainerLayout->setStackingMode(QStackedLayout::StackAll);

_searchCompletion->setFocusPolicy(Qt::NoFocus);
_searchCompletion->setAttribute(Qt::WA_ShowWithoutActivating);
_searchCompletion->setReadOnly(true);
if (_settings.useSystemQtTheme()) {
_searchCompletion->setStyleSheet(_searchCompletion->styleSheet() + QString("background: #00000000;"));
Expand Down Expand Up @@ -513,8 +517,10 @@ void EmojiPickerWindow::reset() {
disable();
}

void EmojiPickerWindow::enable() {
moveQWidgetToCenter(this);
void EmojiPickerWindow::enable(bool resetPosition) {
if (resetPosition) {
moveQWidgetToCenter(this);
}

show();

Expand Down Expand Up @@ -570,7 +576,7 @@ void EmojiPickerWindow::setCursorLocation(const QRect* rect) {
newRect.setY((double)rect->y() / pixelRatio);
newRect.setWidth((double)rect->width() / pixelRatio);
newRect.setHeight((double)rect->height() / pixelRatio);
newRect.setWidth(0);
newRect.setWidth(0); // ????

QPoint newPoint = createPointInScreen(this, newRect);
// newPoint.setX((double)rect->x() / pixelRatio);
Expand All @@ -579,6 +585,44 @@ void EmojiPickerWindow::setCursorLocation(const QRect* rect) {
move(newPoint);
}

QKeyEvent* createKeyEventWithUserPreferences(QEvent::Type _type, int _key, Qt::KeyboardModifiers _modifiers, const QString& _text) {
static std::unordered_map<char, QKeySequence> customHotKeys; // lazy
static bool customHotKeysLoaded = false;
if (!customHotKeysLoaded) {
customHotKeysLoaded = true;

std::unique_ptr<QCoreApplication> dummyApp;
if (QCoreApplication::instance() == nullptr) {
int argc = 0;
char** argv = nullptr;
dummyApp = std::make_unique<QCoreApplication>(argc, argv);
}

customHotKeys = EmojiPickerSettings{}.customHotKeys();
}

for (const auto& [key, target] : customHotKeys) {
if (_text.at(0).toLatin1() == key) {
_key = target[0];
_modifiers = Qt::NoModifier;
if (target[0] & Qt::ShiftModifier) {
_modifiers |= Qt::ShiftModifier;
_key &= ~Qt::ShiftModifier;
}
if (target[0] & Qt::ControlModifier) {
_modifiers |= Qt::ControlModifier;
_key &= ~Qt::ControlModifier;
}
if (target[0] & Qt::AltModifier) {
_modifiers |= Qt::AltModifier;
_key &= ~Qt::AltModifier;
}
}
}

return new QKeyEvent(_type, _key, _modifiers, _text);
}

EmojiAction getEmojiActionForQKeyEvent(const QKeyEvent* event) {
// TODO: ctrl+w = delete word
// TODO: ctrl+d = select word
Expand Down Expand Up @@ -691,8 +735,10 @@ void EmojiPickerWindow::commitEmoji(const Emoji& emoji, bool isRealEmoji, bool c
}
}

void EmojiPickerWindow::processKeyEvent(const QKeyEvent* event) {
EmojiAction action = getEmojiActionForQKeyEvent(event);
void EmojiPickerWindow::processKeyEvent(const QKeyEvent* event, EmojiAction action) {
if (action == EmojiAction::INVALID) {
action = getEmojiActionForQKeyEvent(event);
}

switch (action) {
case EmojiAction::INVALID:
Expand Down Expand Up @@ -839,6 +885,23 @@ void loadScaleFactorFromSettings() {
}
}

std::mutex gui_mutex;
std::condition_variable gui_condition;
bool gui_is_active = false;

void gui_set_active(bool active) {
if (!active) {
// give Qt time to actually close the window before the Qt main thread is blocked
// (assuming 'EmojiCommandDisable' has been dispatched beforehand)
usleep(1000 /*us*/ * 128 /*ms*/);
}

std::unique_lock<decltype(gui_mutex)> lock{gui_mutex};
gui_is_active = active;
lock.unlock();
gui_condition.notify_one();
}

void gui_main(int argc, char** argv) {
QApplication::setOrganizationName(PROJECT_ORGANIZATION);
QApplication::setOrganizationDomain(PROJECT_ORGANIZATION);
Expand All @@ -858,17 +921,26 @@ void gui_main(int argc, char** argv) {

EmojiPickerWindow window;

// TODO: improve the following
QTimer emojiCommandProcessor;
QObject::connect(&emojiCommandProcessor, &QTimer::timeout, [&window]() {
QTimer commandProcessor;
commandProcessor.start(32 /*ms*/);
QObject::connect(&commandProcessor, &QTimer::timeout, [&commandProcessor, &window]() {
// block the entire Qt main thread if gui_is_active == false
std::unique_lock<decltype(gui_mutex)> lock{gui_mutex};
gui_condition.wait(lock, []() { return gui_is_active; });
lock.unlock();

std::shared_ptr<EmojiCommand> _command;
if (emojiCommandQueue.pop(_command)) {
if (auto command = std::dynamic_pointer_cast<EmojiCommandEnable>(_command)) {
window.commitText = command->commitText;
window.enable();
window.enable(command->resetPosition);

commandProcessor.setInterval(4 /*ms*/);
}
if (auto command = std::dynamic_pointer_cast<EmojiCommandDisable>(_command)) {
window.disable();

commandProcessor.setInterval(32 /*ms*/);
}
if (auto command = std::dynamic_pointer_cast<EmojiCommandReset>(_command)) {
window.reset();
Expand All @@ -877,11 +949,10 @@ void gui_main(int argc, char** argv) {
window.setCursorLocation(&*command->rect);
}
if (auto command = std::dynamic_pointer_cast<EmojiCommandProcessKeyEvent>(_command)) {
window.processKeyEvent(&*command->keyEvent);
window.processKeyEvent(&*command->keyEvent, command->action);
}
}
});
emojiCommandProcessor.start(5);

app.exec();
}
Loading
Loading