mirror of
https://github.com/fergalmoran/flameshot.git
synced 2026-02-02 00:27:59 +00:00
resolving merge conflicts
This commit is contained in:
@@ -13,7 +13,44 @@ find_package(
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
add_executable(flameshot)
|
||||
|
||||
# set application icon
|
||||
if (APPLE)
|
||||
# generate iconset
|
||||
execute_process(
|
||||
COMMAND bash "-c" "mkdir -p flameshot.iconset"
|
||||
)
|
||||
execute_process(
|
||||
COMMAND bash "-c" "sips -z 16 16 ../data/img/app/flameshot.png --out flameshot.iconset/icon_16x16.png"
|
||||
COMMAND bash "-c" "sips -z 32 32 ../data/img/app/flameshot.png --out flameshot.iconset/icon_16x16@2x.png"
|
||||
COMMAND bash "-c" "sips -z 32 32 ../data/img/app/flameshot.png --out flameshot.iconset/icon_32x32.png"
|
||||
COMMAND bash "-c" "sips -z 64 64 ../data/img/app/flameshot.png --out flameshot.iconset/icon_32x32@2x.png"
|
||||
COMMAND bash "-c" "sips -z 64 64 ../data/img/app/flameshot.png --out flameshot.iconset/icon_64x64x.png"
|
||||
COMMAND bash "-c" "sips -z 128 128 ../data/img/app/flameshot.png --out flameshot.iconset/icon_64x64@2.png"
|
||||
COMMAND bash "-c" "sips -z 128 128 ../data/img/app/flameshot.png --out flameshot.iconset/icon_128x128.png"
|
||||
COMMAND bash "-c" "sips -z 256 256 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_128x128@2x.png"
|
||||
COMMAND bash "-c" "sips -z 256 256 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_256x256.png"
|
||||
COMMAND bash "-c" "sips -z 512 512 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_256x256@2x.png"
|
||||
COMMAND bash "-c" "sips -z 512 512 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_512x512.png"
|
||||
COMMAND bash "-c" "sips -z 1024 1024 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_512x512@2x.png"
|
||||
COMMAND bash "-c" "iconutil -c icns flameshot.iconset"
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND bash "-c" "rm -R flameshot.iconset"
|
||||
)
|
||||
|
||||
# Set application icon
|
||||
set(MACOSX_BUNDLE_ICON_FILE flameshot.icns)
|
||||
|
||||
# And this part tells CMake where to find and install the file itself
|
||||
set(APP_ICON_MACOSX ${CMAKE_BINARY_DIR}/flameshot.icns)
|
||||
set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
|
||||
add_executable(flameshot MACOSX_BUNDLE main.cpp ${APP_ICON_MACOSX})
|
||||
else()
|
||||
add_executable(flameshot)
|
||||
endif()
|
||||
|
||||
add_executable(Flameshot::flameshot ALIAS flameshot)
|
||||
|
||||
@@ -24,6 +61,7 @@ if(WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(core)
|
||||
@@ -111,16 +149,31 @@ target_include_directories(
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/widgets/capture>
|
||||
$<INSTALL_INTERFACE:include/mylib>)
|
||||
|
||||
target_link_libraries(
|
||||
flameshot
|
||||
project_warnings
|
||||
project_options
|
||||
Qt5::Svg
|
||||
Qt5::DBus
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
SingleApplication::SingleApplication
|
||||
spdlog::spdlog)
|
||||
if (APPLE)
|
||||
target_link_libraries(
|
||||
flameshot
|
||||
project_warnings
|
||||
project_options
|
||||
qhotkey
|
||||
Qt5::Svg
|
||||
Qt5::DBus
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
SingleApplication::SingleApplication
|
||||
spdlog::spdlog)
|
||||
else ()
|
||||
target_link_libraries(
|
||||
flameshot
|
||||
project_warnings
|
||||
project_options
|
||||
Qt5::Svg
|
||||
Qt5::DBus
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
SingleApplication::SingleApplication
|
||||
spdlog::spdlog
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(USE_OPENSSL FALSE)
|
||||
if(ENABLE_OPENSSL)
|
||||
@@ -134,12 +187,14 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT USE_OPENSSL)
|
||||
message(WARNING "OpenSSL is required to upload screenshot to imgur")
|
||||
message(WARNING "OpenSSL is required to upload screenshots")
|
||||
endif()
|
||||
|
||||
target_compile_definitions(flameshot PRIVATE APP_PREFIX="${CMAKE_INSTALL_PREFIX}")
|
||||
target_compile_definitions(flameshot PRIVATE APP_VERSION="v${PROJECT_VERSION}")
|
||||
target_compile_definitions(flameshot PRIVATE IMGUR_CLIENT_ID="313baf0c7b4d3ff")
|
||||
target_compile_definitions(flameshot PRIVATE QAPPLICATION_CLASS=QApplication)
|
||||
target_compile_definitions(flameshot PRIVATE FLAMESHOT_APP_VERSION_URL="${GIT_API_URL}")
|
||||
|
||||
foreach(FILE ${QM_FILES})
|
||||
get_filename_component(F_NAME ${FILE} NAME)
|
||||
@@ -157,10 +212,10 @@ include(GNUInstallDirs)
|
||||
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Flameshot)
|
||||
|
||||
# Install binary
|
||||
install(
|
||||
TARGETS flameshot
|
||||
EXPORT flameshot-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(TARGETS flameshot
|
||||
EXPORT flameshot-targets
|
||||
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# Install desktop files, completion and dbus files
|
||||
configure_file(${CMAKE_SOURCE_DIR}/data/desktopEntry/package/org.flameshot.Flameshot.desktop
|
||||
@@ -246,3 +301,21 @@ if(WIN32)
|
||||
message("Unable to find executable QTDIR/bin/windeployqt.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# macdeployqt
|
||||
if (APPLE)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QTDIR)
|
||||
string(REGEX REPLACE "\n$" "" QTDIR "${QTDIR}")
|
||||
set(MAC_DEPLOY_QT ${QTDIR}/bin/macdeployqt)
|
||||
if (EXISTS ${MAC_DEPLOY_QT})
|
||||
set_source_files_properties(resources/icon.icns PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
set_target_properties(${target} PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
else ()
|
||||
message("Unable to find executable ${MAC_DEPLOY_QT}.")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
@@ -36,8 +36,8 @@ ConfigWindow::ConfigWindow(QWidget* parent)
|
||||
: QTabWidget(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
const int size = GlobalValues::buttonBaseSize() * 12;
|
||||
setMinimumSize(size, size);
|
||||
setMinimumSize(GlobalValues::buttonBaseSize() * 15,
|
||||
GlobalValues::buttonBaseSize() * 12);
|
||||
setWindowIcon(QIcon(":img/app/flameshot.svg"));
|
||||
setWindowTitle(tr("Configuration"));
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ void GeneneralConf::changeSavePath()
|
||||
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
|
||||
if (!path.isEmpty()) {
|
||||
m_savePath->setText(path);
|
||||
ConfigHandler().setSaveAfterCopyPath(path);
|
||||
ConfigHandler().setSavePath(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ ShortcutsWidget::ShortcutsWidget(QWidget* parent)
|
||||
#endif
|
||||
|
||||
m_layout = new QVBoxLayout(this);
|
||||
m_layout->setAlignment(Qt::AlignHCenter);
|
||||
m_layout->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
|
||||
m_shortcuts = m_config.shortcuts();
|
||||
initInfoTable();
|
||||
@@ -90,10 +90,13 @@ void ShortcutsWidget::initInfoTable()
|
||||
const auto default_key_sequence = current_shortcut.at(2);
|
||||
m_table->setItem(i, 0, new QTableWidgetItem(description));
|
||||
|
||||
const auto key_sequence = identifier.isEmpty()
|
||||
? default_key_sequence
|
||||
: m_config.shortcut(identifier);
|
||||
QTableWidgetItem* item = new QTableWidgetItem(key_sequence);
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QTableWidgetItem* item =
|
||||
new QTableWidgetItem(nativeOSHotKeyText(m_shortcuts.at(i).at(2)));
|
||||
#else
|
||||
QTableWidgetItem* item = new QTableWidgetItem(m_shortcuts.at(i).at(2));
|
||||
#endif
|
||||
item->setTextAlignment(Qt::AlignCenter);
|
||||
m_table->setItem(i, 1, item);
|
||||
|
||||
@@ -117,10 +120,8 @@ void ShortcutsWidget::initInfoTable()
|
||||
// adjust size
|
||||
m_table->resizeColumnsToContents();
|
||||
m_table->resizeRowsToContents();
|
||||
m_table->setMinimumWidth(400);
|
||||
m_table->setMaximumWidth(600);
|
||||
|
||||
m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
m_table->horizontalHeader()->setMinimumSectionSize(200);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
m_table->horizontalHeader()->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Expanding);
|
||||
}
|
||||
@@ -145,8 +146,14 @@ void ShortcutsWidget::slotShortcutCellClicked(int row, int col)
|
||||
}
|
||||
|
||||
if (m_config.setShortcut(shortcutName, shortcutValue.toString())) {
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QTableWidgetItem* item = new QTableWidgetItem(
|
||||
nativeOSHotKeyText(shortcutValue.toString()));
|
||||
#else
|
||||
QTableWidgetItem* item =
|
||||
new QTableWidgetItem(shortcutValue.toString());
|
||||
#endif
|
||||
item->setTextAlignment(Qt::AlignCenter);
|
||||
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
|
||||
m_table->setItem(row, col, item);
|
||||
@@ -155,3 +162,16 @@ void ShortcutsWidget::slotShortcutCellClicked(int row, int col)
|
||||
delete setShortcutDialog;
|
||||
}
|
||||
}
|
||||
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
const QString& ShortcutsWidget::nativeOSHotKeyText(const QString& text)
|
||||
{
|
||||
m_res = text;
|
||||
m_res.replace("Ctrl+", "⌘");
|
||||
m_res.replace("Alt+", "⌥");
|
||||
m_res.replace("Meta+", "⌃");
|
||||
m_res.replace("Shift+", "⇧");
|
||||
return m_res;
|
||||
}
|
||||
#endif
|
||||
@@ -36,11 +36,19 @@ public:
|
||||
|
||||
private:
|
||||
void initInfoTable();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
const QString& nativeOSHotKeyText(const QString& text);
|
||||
#endif
|
||||
|
||||
private slots:
|
||||
void slotShortcutCellClicked(int, int);
|
||||
|
||||
private:
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QString m_res;
|
||||
#endif
|
||||
ConfigHandler m_config;
|
||||
QTableWidget* m_table;
|
||||
QVBoxLayout* m_layout;
|
||||
|
||||
@@ -51,22 +51,34 @@ QMap<QString, QString> StrftimeChooserWidget::m_buttonData{
|
||||
{ QT_TR_NOOP("Century (00-99)"), "%C" },
|
||||
{ QT_TR_NOOP("Year (00-99)"), "%y" },
|
||||
{ QT_TR_NOOP("Year (2000)"), "%Y" },
|
||||
#ifndef Q_OS_WIN
|
||||
// TODO - fix localized names on windows (ex. Cyrillic)
|
||||
{ QT_TR_NOOP("Month Name (jan)"), "%b" },
|
||||
{ QT_TR_NOOP("Month Name (january)"), "%B" },
|
||||
#endif
|
||||
{ QT_TR_NOOP("Month (01-12)"), "%m" },
|
||||
{ QT_TR_NOOP("Week Day (1-7)"), "%u" },
|
||||
{ QT_TR_NOOP("Week (01-53)"), "%V" },
|
||||
#ifndef Q_OS_WIN
|
||||
// TODO - fix localized names on windows (ex. Cyrillic)
|
||||
{ QT_TR_NOOP("Day Name (mon)"), "%a" },
|
||||
{ QT_TR_NOOP("Day Name (monday)"), "%A" },
|
||||
#endif
|
||||
{ QT_TR_NOOP("Day (01-31)"), "%d" },
|
||||
{ QT_TR_NOOP("Day of Month (1-31)"), "%e" },
|
||||
{ QT_TR_NOOP("Day (001-366)"), "%j" },
|
||||
#ifndef Q_OS_WIN
|
||||
// TODO - fix localized names on windows (ex. Cyrillic)
|
||||
{ QT_TR_NOOP("Time (%H-%M-%S)"), "%T" },
|
||||
{ QT_TR_NOOP("Time (%H-%M)"), "%R" },
|
||||
#endif
|
||||
{ QT_TR_NOOP("Hour (00-23)"), "%H" },
|
||||
{ QT_TR_NOOP("Hour (01-12)"), "%I" },
|
||||
{ QT_TR_NOOP("Minute (00-59)"), "%M" },
|
||||
{ QT_TR_NOOP("Second (00-59)"), "%S" },
|
||||
#ifndef Q_OS_WIN
|
||||
// TODO - fix localized names on windows (ex. Cyrillic)
|
||||
{ QT_TR_NOOP("Full Date (%m/%d/%y)"), "%D" },
|
||||
#endif
|
||||
{ QT_TR_NOOP("Full Date (%Y-%m-%d)"), "%F" },
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
|
||||
# Required to generate MOC
|
||||
IF (APPLE)
|
||||
add_subdirectory(QHotkey)
|
||||
ENDIF ()
|
||||
|
||||
target_sources(flameshot PRIVATE controller.h flameshotdbusadapter.h)
|
||||
|
||||
target_sources(flameshot PRIVATE capturerequest.cpp controller.cpp flameshotdbusadapter.cpp)
|
||||
|
||||
IF(WIN32)
|
||||
IF (WIN32)
|
||||
target_sources(flameshot PRIVATE globalshortcutfilter.h globalshortcutfilter.cpp)
|
||||
ENDIF()
|
||||
ENDIF ()
|
||||
|
||||
43
src/core/QHotkey/CMakeLists.txt
Normal file
43
src/core/QHotkey/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
|
||||
|
||||
qt5_wrap_cpp(MOC_HEADERS
|
||||
qhotkey.h
|
||||
qhotkey_p.h)
|
||||
|
||||
set(LIBS
|
||||
Qt5::Core
|
||||
Qt5::Widgets)
|
||||
|
||||
set(SRC_FILES
|
||||
qhotkey.cpp)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
|
||||
find_library(CARBON_LIBRARY Carbon)
|
||||
mark_as_advanced(CARBON_LIBRARY)
|
||||
|
||||
set(SRC_FILES ${SRC_FILES} qhotkey_mac.cpp)
|
||||
set(LIBS ${LIBS} ${CARBON_LIBRARY})
|
||||
elseif(WIN32)
|
||||
set(SRC_FILES ${SRC_FILES} qhotkey_win.cpp)
|
||||
else()
|
||||
find_package(X11 REQUIRED)
|
||||
find_package(Qt5X11Extras REQUIRED)
|
||||
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
set(LIBS ${LIBS} ${X11_LIBRARIES} Qt5::X11Extras)
|
||||
set(SRC_FILES ${SRC_FILES} qhotkey_x11.cpp)
|
||||
endif()
|
||||
|
||||
add_library(qhotkey ${SRC_FILES} ${MOC_HEADERS})
|
||||
add_library(QHotkey::QHotkey ALIAS qhotkey)
|
||||
target_link_libraries(qhotkey ${LIBS})
|
||||
|
||||
target_include_directories(qhotkey
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
27
src/core/QHotkey/LICENSE
Normal file
27
src/core/QHotkey/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2016, Felix Barz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of QHotkey nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
src/core/QHotkey/QHotkey
Normal file
1
src/core/QHotkey/QHotkey
Normal file
@@ -0,0 +1 @@
|
||||
#include "qhotkey.h"
|
||||
104
src/core/QHotkey/README.md
Normal file
104
src/core/QHotkey/README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# QHotkey
|
||||
A global shortcut/hotkey for Desktop Qt-Applications.
|
||||
|
||||
The QHotkey is a class that can be used to create hotkeys/global shortcuts, aka shortcuts that work everywhere, independent of the application state. This means your application can be active, inactive, minimized or not visible at all and still receive the shortcuts.
|
||||
|
||||
## Features
|
||||
- Works on Windows, Mac and X11
|
||||
- Easy to use, can use `QKeySequence` for easy shortcut input
|
||||
- Supports almost all common keys (Depends on OS & Keyboard-Layout)
|
||||
- Allows direct input of Key/Modifier-Combinations
|
||||
- Supports multiple QHotkey-instances for the same shortcut (with optimisations)
|
||||
- Thread-Safe - Can be used on all threads (See section Thread safety)
|
||||
- Allows usage of native keycodes and modifiers, if needed
|
||||
|
||||
**Note:** For now Wayland is not supported, as it is simply not possible to register a global shortcut with wayland. For more details, or possible Ideas on how to get Hotkeys working on wayland, see [Issue #14](https://github.com/Skycoder42/QHotkey/issues/14).
|
||||
|
||||
## Installation
|
||||
The package is providet as qpm package, [`de.skycoder42.qhotkey`](https://www.qpm.io/packages/de.skycoder42.qhotkey/index.html). You can install it either via qpmx (preferred) or directly via qpm.
|
||||
|
||||
### Via qpmx
|
||||
[qpmx](https://github.com/Skycoder42/qpmx) is a frontend for qpm (and other tools) with additional features, and is the preferred way to install packages. To use it:
|
||||
|
||||
1. Install qpmx (See [GitHub - Installation](https://github.com/Skycoder42/qpmx#installation))
|
||||
2. Install qpm (See [GitHub - Installing](https://github.com/Cutehacks/qpm/blob/master/README.md#installing), for **windows** see below)
|
||||
3. In your projects root directory, run `qpmx install de.skycoder42.qhotkey`
|
||||
|
||||
### Via qpm
|
||||
|
||||
1. Install qpm (See [GitHub - Installing](https://github.com/Cutehacks/qpm/blob/master/README.md#installing), for **windows** see below)
|
||||
2. In your projects root directory, run `qpm install de.skycoder42.qhotkey`
|
||||
3. Include qpm to your project by adding `include(vendor/vendor.pri)` to your `.pro` file
|
||||
|
||||
Check their [GitHub - Usage for App Developers](https://github.com/Cutehacks/qpm/blob/master/README.md#usage-for-app-developers) to learn more about qpm.
|
||||
|
||||
**Important for Windows users:** QPM Version *0.10.0* (the one you can download on the website) is currently broken on windows! It's already fixed in master, but not released yet. Until a newer versions gets released, you can download the latest dev build from here:
|
||||
- https://storage.googleapis.com/www.qpm.io/download/latest/windows_amd64/qpm.exe
|
||||
- https://storage.googleapis.com/www.qpm.io/download/latest/windows_386/qpm.exe
|
||||
|
||||
## Usage
|
||||
The general usage is to create QHotkey instances for specific hotkeys, register them and then simply connect to the signal emitted once the hotkey is pressed.
|
||||
|
||||
### Example
|
||||
The following example shows a simple application that will run without a window in the background until you press the key-combination <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Q</kbd> (<kbd>⌘</kbd>+<kbd>⌥</kbd>+<kbd>Q</kbd> on Mac). This will quit the application. The debug output will tell if the hotkey was successfully registered and that it was pressed.
|
||||
```cpp
|
||||
#include <QHotkey>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
auto hotkey = new QHotkey(QKeySequence("ctrl+alt+Q"), true, &a);//The hotkey will be automatically registered
|
||||
qDebug() << "Is Registered: " << hotkey->isRegistered();
|
||||
|
||||
QObject::connect(hotkey, &QHotkey::activated, qApp, [&](){
|
||||
qDebug() << "Hotkey Activated - the application will quit now";
|
||||
qApp->quit();
|
||||
});
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** You need the .pri include for this to work.
|
||||
|
||||
### Testing
|
||||
By running the example in `./HotkeyTest` you can test out the QHotkey class. There are 4 sections:
|
||||
- **Playground:** You can enter some sequences here and try it out with different key combinations.
|
||||
- **Testings:** A list of selected hotkeys. Activate it and try out which ones work for you (*Hint:* Depending on OS and keyboard layout, it's very possible that a few don't work).
|
||||
- **Threading:** Activate the checkbox to move 2 Hotkeys of the playground to seperate threads. It should work without a difference.
|
||||
- **Native Shortcut**: Allows you to try out the direct usage of native shortcuts
|
||||
|
||||
### Logging
|
||||
By default, QHotkey prints some warning messages if something goes wrong (For example, a key that cannot be translated). All messages of QHotkey are grouped into the [QLoggingCategory](https://doc.qt.io/qt-5/qloggingcategory.html) `"QHotkey"`. If you want to simply disable the logging, call the folling function somewhere in your code:
|
||||
```cpp
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("QHotkey.warning=false"));
|
||||
```
|
||||
This will turn all warnings of QHotkey of (It only uses warnings for now, thats why this is enough). For more information about all the things you can do with the logging categories, check the Qt-Documentation
|
||||
|
||||
## Thread saftey
|
||||
The QHotkey class itself is reentrant - wich means you can create as many instances as required on any thread. This allows you to use the QHotkey on all threads. **But** you should never use the QHotkey instance on a thread that is different from the one the instance belongs to! Internally the system uses a singleton instance that handles the hotkey events and distributes them to the QHotkey instances. This internal class is completley threadsafe.
|
||||
|
||||
However, this singleton instance only runs on the main thread. (One reason is that some of the OS-Functions are not thread safe). To make threaded hotkeys possible, the critical functions (registering/unregistering hotkeys and keytranslation) are all run on the mainthread too. The QHotkey instances on other threads use `QMetaObject::invokeMethod` with a `Qt::BlockingQueuedConnection`.
|
||||
|
||||
For you this means: QHotkey instances on other threads than the main thread may take a little longer to register/unregister/translate hotkeys, because they have to wait for the main thread to do this for them. **Important:** there is however, one additional limitation that comes with that feature: QHotkey instances on other threads but the main thread *must* be unregistered or destroyed *before* the main eventloop ends. Otherwise, your application will hangup on destruction of the hotkey. This limitation does not apply for instances on the main thread. Furthermore, the same happens if you change the shortcut or register/unregister before the loop started, until it actually starts.
|
||||
|
||||
## Documentation
|
||||
The documentation is available as release and on [github pages](https://skycoder42.github.io/QHotkey/).
|
||||
|
||||
The documentation was created using [doxygen](http://www.doxygen.org). It includes an HTML-documentation and Qt-Help files that can be included into QtCreator (QtAssistant) to show F1-Help (See [Adding External Documentation](https://doc.qt.io/qtcreator/creator-help.html#adding-external-documentation) for more details).
|
||||
|
||||
## Technical
|
||||
### Requirements
|
||||
- Explicit support is only given down to the latest Qt LTS, but may work with earlier versions, too
|
||||
- At least the QtGui-Module (a QGuiApplication). Hotkeys on console based applications are not supported (By the operating systems). You can however create a gui application without any visible window.
|
||||
- C++11
|
||||
|
||||
### Known Limitations
|
||||
- Only single key/modifier combinations are possible. If using QKeySequence, only the first key+modifier of the sequence will be used.
|
||||
- Qt::Key makes no difference between normal numbers and the Numpad numbers. Most keyboards however require this. Thus, you can't register shortcuts for the numpad, unless you use a native shortcut.
|
||||
- Supports not all keys, but most of the common ones. There are differences between platforms and it depends on the Keyboard-Layout. "Delete", for example, works on windows and mac, but not on X11 (At least on my test machines). I tried to use OS-Functions where possible, but since the Qt::Key values need to be converted into native keys, there are some limitations. I can use need such a key, try using the native shortcut.
|
||||
- The registered keys will be "taken" by QHotkey. This means after a hotkey was cosumend by your application, it will not be sent to the active application. This is done this way by the operating systems and cannot be changed.
|
||||
- If you get a `QHotkey: Failed to register hotkey. Error: BadAccess (attempt to access private resource denied)` error on X11, this means you are trying to register a hotkey that is private to X11. Those keys simply cannot be registered using the normal API
|
||||
391
src/core/QHotkey/qhotkey.cpp
Normal file
391
src/core/QHotkey/qhotkey.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
#include "qhotkey.h"
|
||||
#include "qhotkey_p.h"
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QMetaMethod>
|
||||
#include <QThread>
|
||||
|
||||
Q_LOGGING_CATEGORY(logQHotkey, "QHotkey")
|
||||
|
||||
void QHotkey::addGlobalMapping(const QKeySequence& shortcut,
|
||||
QHotkey::NativeShortcut nativeShortcut)
|
||||
{
|
||||
QMetaObject::invokeMethod(
|
||||
QHotkeyPrivate::instance(),
|
||||
"addMappingInvoked",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(Qt::Key, Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask)),
|
||||
Q_ARG(Qt::KeyboardModifiers,
|
||||
Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask)),
|
||||
Q_ARG(QHotkey::NativeShortcut, nativeShortcut));
|
||||
}
|
||||
|
||||
bool QHotkey::isPlatformSupported()
|
||||
{
|
||||
return QHotkeyPrivate::isPlatformSupported();
|
||||
}
|
||||
|
||||
QHotkey::QHotkey(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _keyCode(Qt::Key_unknown)
|
||||
, _modifiers(Qt::NoModifier)
|
||||
, _registered(false)
|
||||
{}
|
||||
|
||||
QHotkey::QHotkey(const QKeySequence& shortcut,
|
||||
bool autoRegister,
|
||||
QObject* parent)
|
||||
: QHotkey(parent)
|
||||
{
|
||||
setShortcut(shortcut, autoRegister);
|
||||
}
|
||||
|
||||
QHotkey::QHotkey(Qt::Key keyCode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
bool autoRegister,
|
||||
QObject* parent)
|
||||
: QHotkey(parent)
|
||||
{
|
||||
setShortcut(keyCode, modifiers, autoRegister);
|
||||
}
|
||||
|
||||
QHotkey::QHotkey(QHotkey::NativeShortcut shortcut,
|
||||
bool autoRegister,
|
||||
QObject* parent)
|
||||
: QHotkey(parent)
|
||||
{
|
||||
setNativeShortcut(shortcut, autoRegister);
|
||||
}
|
||||
|
||||
QHotkey::~QHotkey()
|
||||
{
|
||||
if (_registered)
|
||||
QHotkeyPrivate::instance()->removeShortcut(this);
|
||||
}
|
||||
|
||||
QKeySequence QHotkey::shortcut() const
|
||||
{
|
||||
if (_keyCode == Qt::Key_unknown)
|
||||
return QKeySequence();
|
||||
return QKeySequence(static_cast<int>(_keyCode | _modifiers));
|
||||
}
|
||||
|
||||
Qt::Key QHotkey::keyCode() const
|
||||
{
|
||||
return _keyCode;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers QHotkey::modifiers() const
|
||||
{
|
||||
return _modifiers;
|
||||
}
|
||||
|
||||
QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const
|
||||
{
|
||||
return _nativeShortcut;
|
||||
}
|
||||
|
||||
bool QHotkey::isRegistered() const
|
||||
{
|
||||
return _registered;
|
||||
}
|
||||
|
||||
bool QHotkey::setShortcut(const QKeySequence& shortcut, bool autoRegister)
|
||||
{
|
||||
if (shortcut.isEmpty())
|
||||
return resetShortcut();
|
||||
if (shortcut.count() > 1) {
|
||||
qCWarning(logQHotkey,
|
||||
"Keysequences with multiple shortcuts are not allowed! "
|
||||
"Only the first shortcut will be used!");
|
||||
}
|
||||
|
||||
return setShortcut(
|
||||
Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask),
|
||||
Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),
|
||||
autoRegister);
|
||||
}
|
||||
|
||||
bool QHotkey::setShortcut(Qt::Key keyCode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
bool autoRegister)
|
||||
{
|
||||
if (_registered) {
|
||||
if (autoRegister) {
|
||||
if (!QHotkeyPrivate::instance()->removeShortcut(this))
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode == Qt::Key_unknown) {
|
||||
_keyCode = Qt::Key_unknown;
|
||||
_modifiers = Qt::NoModifier;
|
||||
_nativeShortcut = NativeShortcut();
|
||||
return true;
|
||||
}
|
||||
|
||||
_keyCode = keyCode;
|
||||
_modifiers = modifiers;
|
||||
_nativeShortcut =
|
||||
QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);
|
||||
if (_nativeShortcut.isValid()) {
|
||||
if (autoRegister)
|
||||
return QHotkeyPrivate::instance()->addShortcut(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:"
|
||||
<< keyCode << "Modifiers:" << modifiers;
|
||||
_keyCode = Qt::Key_unknown;
|
||||
_modifiers = Qt::NoModifier;
|
||||
_nativeShortcut = NativeShortcut();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHotkey::resetShortcut()
|
||||
{
|
||||
if (_registered && !QHotkeyPrivate::instance()->removeShortcut(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_keyCode = Qt::Key_unknown;
|
||||
_modifiers = Qt::NoModifier;
|
||||
_nativeShortcut = NativeShortcut();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut,
|
||||
bool autoRegister)
|
||||
{
|
||||
if (_registered) {
|
||||
if (autoRegister) {
|
||||
if (!QHotkeyPrivate::instance()->removeShortcut(this))
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nativeShortcut.isValid()) {
|
||||
_keyCode = Qt::Key_unknown;
|
||||
_modifiers = Qt::NoModifier;
|
||||
_nativeShortcut = nativeShortcut;
|
||||
if (autoRegister)
|
||||
return QHotkeyPrivate::instance()->addShortcut(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
_keyCode = Qt::Key_unknown;
|
||||
_modifiers = Qt::NoModifier;
|
||||
_nativeShortcut = NativeShortcut();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkey::setRegistered(bool registered)
|
||||
{
|
||||
if (_registered && !registered)
|
||||
return QHotkeyPrivate::instance()->removeShortcut(this);
|
||||
if (!_registered && registered) {
|
||||
if (!_nativeShortcut.isValid())
|
||||
return false;
|
||||
return QHotkeyPrivate::instance()->addShortcut(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------- QHotkeyPrivate implementation ----------
|
||||
|
||||
QHotkeyPrivate::QHotkeyPrivate()
|
||||
{
|
||||
Q_ASSERT_X(qApp,
|
||||
Q_FUNC_INFO,
|
||||
"QHotkey requires QCoreApplication to be instantiated");
|
||||
qApp->eventDispatcher()->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
QHotkeyPrivate::~QHotkeyPrivate()
|
||||
{
|
||||
if (!shortcuts.isEmpty())
|
||||
qCWarning(logQHotkey)
|
||||
<< "QHotkeyPrivate destroyed with registered shortcuts!";
|
||||
if (qApp && qApp->eventDispatcher())
|
||||
qApp->eventDispatcher()->removeNativeEventFilter(this);
|
||||
}
|
||||
|
||||
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(
|
||||
Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Qt::ConnectionType conType =
|
||||
(QThread::currentThread() == thread() ? Qt::DirectConnection
|
||||
: Qt::BlockingQueuedConnection);
|
||||
QHotkey::NativeShortcut res;
|
||||
if (!QMetaObject::invokeMethod(this,
|
||||
"nativeShortcutInvoked",
|
||||
conType,
|
||||
Q_RETURN_ARG(QHotkey::NativeShortcut, res),
|
||||
Q_ARG(Qt::Key, keycode),
|
||||
Q_ARG(Qt::KeyboardModifiers, modifiers))) {
|
||||
return QHotkey::NativeShortcut();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivate::addShortcut(QHotkey* hotkey)
|
||||
{
|
||||
if (hotkey->_registered)
|
||||
return false;
|
||||
|
||||
Qt::ConnectionType conType =
|
||||
(QThread::currentThread() == thread() ? Qt::DirectConnection
|
||||
: Qt::BlockingQueuedConnection);
|
||||
bool res = false;
|
||||
if (!QMetaObject::invokeMethod(this,
|
||||
"addShortcutInvoked",
|
||||
conType,
|
||||
Q_RETURN_ARG(bool, res),
|
||||
Q_ARG(QHotkey*, hotkey))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res)
|
||||
emit hotkey->registeredChanged(true);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivate::removeShortcut(QHotkey* hotkey)
|
||||
{
|
||||
if (!hotkey->_registered)
|
||||
return false;
|
||||
|
||||
Qt::ConnectionType conType =
|
||||
(QThread::currentThread() == thread() ? Qt::DirectConnection
|
||||
: Qt::BlockingQueuedConnection);
|
||||
bool res = false;
|
||||
if (!QMetaObject::invokeMethod(this,
|
||||
"removeShortcutInvoked",
|
||||
conType,
|
||||
Q_RETURN_ARG(bool, res),
|
||||
Q_ARG(QHotkey*, hotkey))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res)
|
||||
emit hotkey->registeredChanged(false);
|
||||
return res;
|
||||
}
|
||||
|
||||
void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated);
|
||||
for (QHotkey* hkey : shortcuts.values(shortcut))
|
||||
signal.invoke(hkey, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released);
|
||||
for (QHotkey* hkey : shortcuts.values(shortcut))
|
||||
signal.invoke(hkey, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QHotkey::NativeShortcut nativeShortcut)
|
||||
{
|
||||
mapping.insert({ keycode, modifiers }, nativeShortcut);
|
||||
}
|
||||
|
||||
bool QHotkeyPrivate::addShortcutInvoked(QHotkey* hotkey)
|
||||
{
|
||||
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
|
||||
|
||||
if (!shortcuts.contains(shortcut)) {
|
||||
if (!registerShortcut(shortcut)) {
|
||||
qCWarning(logQHotkey)
|
||||
<< QHotkey::tr("Failed to register %1. Error: %2")
|
||||
.arg(hotkey->shortcut().toString(), error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
shortcuts.insert(shortcut, hotkey);
|
||||
hotkey->_registered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivate::removeShortcutInvoked(QHotkey* hotkey)
|
||||
{
|
||||
QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
|
||||
|
||||
if (shortcuts.remove(shortcut, hotkey) == 0)
|
||||
return false;
|
||||
hotkey->_registered = false;
|
||||
emit hotkey->registeredChanged(true);
|
||||
if (shortcuts.count(shortcut) == 0) {
|
||||
if (!unregisterShortcut(shortcut)) {
|
||||
qCWarning(logQHotkey)
|
||||
<< QHotkey::tr("Failed to unregister %1. Error: %2")
|
||||
.arg(hotkey->shortcut().toString(), error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(
|
||||
Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
if (mapping.contains({ keycode, modifiers }))
|
||||
return mapping.value({ keycode, modifiers });
|
||||
|
||||
bool ok1 = false;
|
||||
auto k = nativeKeycode(keycode, ok1);
|
||||
bool ok2 = false;
|
||||
auto m = nativeModifiers(modifiers, ok2);
|
||||
if (ok1 && ok2)
|
||||
return { k, m };
|
||||
return {};
|
||||
}
|
||||
|
||||
QHotkey::NativeShortcut::NativeShortcut()
|
||||
: key()
|
||||
, modifier()
|
||||
, valid(false)
|
||||
{}
|
||||
|
||||
QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier)
|
||||
: key(key)
|
||||
, modifier(modifier)
|
||||
, valid(true)
|
||||
{}
|
||||
|
||||
bool QHotkey::NativeShortcut::isValid() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool QHotkey::NativeShortcut::operator==(QHotkey::NativeShortcut other) const
|
||||
{
|
||||
return (key == other.key) && (modifier == other.modifier) &&
|
||||
valid == other.valid;
|
||||
}
|
||||
|
||||
bool QHotkey::NativeShortcut::operator!=(QHotkey::NativeShortcut other) const
|
||||
{
|
||||
return (key != other.key) || (modifier != other.modifier) ||
|
||||
valid != other.valid;
|
||||
}
|
||||
|
||||
uint qHash(QHotkey::NativeShortcut key)
|
||||
{
|
||||
return qHash(key.key) ^ qHash(key.modifier);
|
||||
}
|
||||
|
||||
uint qHash(QHotkey::NativeShortcut key, uint seed)
|
||||
{
|
||||
return qHash(key.key, seed) ^ qHash(key.modifier, seed);
|
||||
}
|
||||
138
src/core/QHotkey/qhotkey.h
Normal file
138
src/core/QHotkey/qhotkey.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef QHOTKEY_H
|
||||
#define QHOTKEY_H
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QLoggingCategory>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
|
||||
#ifdef QHOTKEY_LIB
|
||||
#ifdef QHOTKEY_LIB_BUILD
|
||||
#define QHOTKEY_SHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define QHOTKEY_SHARED_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
#else
|
||||
#define QHOTKEY_SHARED_EXPORT
|
||||
#endif
|
||||
|
||||
//! A class to define global, systemwide Hotkeys
|
||||
class QHOTKEY_SHARED_EXPORT QHotkey : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
//! @private
|
||||
friend class QHotkeyPrivate;
|
||||
|
||||
//! Specifies whether this hotkey is currently registered or not
|
||||
Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY
|
||||
registeredChanged)
|
||||
//! Holds the shortcut this hotkey will be triggered on
|
||||
Q_PROPERTY(
|
||||
QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut)
|
||||
|
||||
public:
|
||||
//! Defines shortcut with native keycodes
|
||||
class QHOTKEY_SHARED_EXPORT NativeShortcut
|
||||
{
|
||||
public:
|
||||
//! The native keycode
|
||||
quint32 key;
|
||||
//! The native modifiers
|
||||
quint32 modifier;
|
||||
|
||||
//! Creates an invalid native shortcut
|
||||
NativeShortcut();
|
||||
//! Creates a valid native shortcut, with the given key and modifiers
|
||||
NativeShortcut(quint32 key, quint32 modifier = 0);
|
||||
|
||||
//! Checks, whether this shortcut is valid or not
|
||||
bool isValid() const;
|
||||
|
||||
//! Equality operator
|
||||
bool operator==(NativeShortcut other) const;
|
||||
//! Inequality operator
|
||||
bool operator!=(NativeShortcut other) const;
|
||||
|
||||
private:
|
||||
bool valid;
|
||||
};
|
||||
|
||||
//! Adds a global mapping of a key sequence to a replacement native shortcut
|
||||
static void addGlobalMapping(const QKeySequence& shortcut,
|
||||
NativeShortcut nativeShortcut);
|
||||
|
||||
//! Checks if global shortcuts are supported by the current platform
|
||||
static bool isPlatformSupported();
|
||||
|
||||
//! Default Constructor
|
||||
explicit QHotkey(QObject* parent = nullptr);
|
||||
//! Constructs a hotkey with a shortcut and optionally registers it
|
||||
explicit QHotkey(const QKeySequence& shortcut,
|
||||
bool autoRegister = false,
|
||||
QObject* parent = nullptr);
|
||||
//! Constructs a hotkey with a key and modifiers and optionally registers it
|
||||
explicit QHotkey(Qt::Key keyCode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
bool autoRegister = false,
|
||||
QObject* parent = nullptr);
|
||||
//! Constructs a hotkey from a native shortcut and optionally registers it
|
||||
explicit QHotkey(NativeShortcut shortcut,
|
||||
bool autoRegister = false,
|
||||
QObject* parent = nullptr);
|
||||
~QHotkey() override;
|
||||
|
||||
//! @readAcFn{QHotkey::registered}
|
||||
bool isRegistered() const;
|
||||
//! @readAcFn{QHotkey::shortcut}
|
||||
QKeySequence shortcut() const;
|
||||
//! @readAcFn{QHotkey::shortcut} - the key only
|
||||
Qt::Key keyCode() const;
|
||||
//! @readAcFn{QHotkey::shortcut} - the modifiers only
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
|
||||
//! Get the current native shortcut
|
||||
NativeShortcut currentNativeShortcut() const;
|
||||
|
||||
public slots:
|
||||
//! @writeAcFn{QHotkey::registered}
|
||||
bool setRegistered(bool registered);
|
||||
|
||||
//! @writeAcFn{QHotkey::shortcut}
|
||||
bool setShortcut(const QKeySequence& shortcut, bool autoRegister = false);
|
||||
//! @writeAcFn{QHotkey::shortcut}
|
||||
bool setShortcut(Qt::Key keyCode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
bool autoRegister = false);
|
||||
//! @resetAcFn{QHotkey::shortcut}
|
||||
bool resetShortcut();
|
||||
|
||||
//! Set this hotkey to a native shortcut
|
||||
bool setNativeShortcut(QHotkey::NativeShortcut nativeShortcut,
|
||||
bool autoRegister = false);
|
||||
|
||||
signals:
|
||||
//! Will be emitted if the shortcut is pressed
|
||||
void activated(QPrivateSignal);
|
||||
|
||||
//! Will be emitted if the shortcut press is released
|
||||
void released(QPrivateSignal);
|
||||
|
||||
//! @notifyAcFn{QHotkey::registered}
|
||||
void registeredChanged(bool registered);
|
||||
|
||||
private:
|
||||
Qt::Key _keyCode;
|
||||
Qt::KeyboardModifiers _modifiers;
|
||||
|
||||
NativeShortcut _nativeShortcut;
|
||||
bool _registered;
|
||||
};
|
||||
|
||||
uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key);
|
||||
uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key, uint seed);
|
||||
|
||||
QHOTKEY_SHARED_EXPORT Q_DECLARE_LOGGING_CATEGORY(logQHotkey)
|
||||
|
||||
Q_DECLARE_METATYPE(QHotkey::NativeShortcut)
|
||||
|
||||
#endif // QHOTKEY_H
|
||||
328
src/core/QHotkey/qhotkey_mac.cpp
Normal file
328
src/core/QHotkey/qhotkey_mac.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
#include "qhotkey.h"
|
||||
#include "qhotkey_p.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <QDebug>
|
||||
|
||||
class QHotkeyPrivateMac : public QHotkeyPrivate
|
||||
{
|
||||
public:
|
||||
// QAbstractNativeEventFilter interface
|
||||
bool nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result) Q_DECL_OVERRIDE;
|
||||
|
||||
static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler,
|
||||
EventRef event,
|
||||
void* data);
|
||||
static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler,
|
||||
EventRef event,
|
||||
void* data);
|
||||
|
||||
protected:
|
||||
// QHotkeyPrivate interface
|
||||
quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE;
|
||||
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok) Q_DECL_OVERRIDE;
|
||||
bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
static bool isHotkeyHandlerRegistered;
|
||||
static QHash<QHotkey::NativeShortcut, EventHotKeyRef> hotkeyRefs;
|
||||
};
|
||||
NATIVE_INSTANCE(QHotkeyPrivateMac)
|
||||
|
||||
bool QHotkeyPrivate::isPlatformSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
|
||||
QHash<QHotkey::NativeShortcut, EventHotKeyRef> QHotkeyPrivateMac::hotkeyRefs;
|
||||
|
||||
bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result)
|
||||
{
|
||||
Q_UNUSED(eventType)
|
||||
Q_UNUSED(message)
|
||||
Q_UNUSED(result)
|
||||
return false;
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool& ok)
|
||||
{
|
||||
// Constants found in NSEvent.h from AppKit.framework
|
||||
ok = true;
|
||||
switch (keycode) {
|
||||
case Qt::Key_Return:
|
||||
return kVK_Return;
|
||||
case Qt::Key_Enter:
|
||||
return kVK_ANSI_KeypadEnter;
|
||||
case Qt::Key_Tab:
|
||||
return kVK_Tab;
|
||||
case Qt::Key_Space:
|
||||
return kVK_Space;
|
||||
case Qt::Key_Backspace:
|
||||
return kVK_Delete;
|
||||
case Qt::Key_Escape:
|
||||
return kVK_Escape;
|
||||
case Qt::Key_CapsLock:
|
||||
return kVK_CapsLock;
|
||||
case Qt::Key_Option:
|
||||
return kVK_Option;
|
||||
case Qt::Key_F17:
|
||||
return kVK_F17;
|
||||
case Qt::Key_VolumeUp:
|
||||
return kVK_VolumeUp;
|
||||
case Qt::Key_VolumeDown:
|
||||
return kVK_VolumeDown;
|
||||
case Qt::Key_F18:
|
||||
return kVK_F18;
|
||||
case Qt::Key_F19:
|
||||
return kVK_F19;
|
||||
case Qt::Key_F20:
|
||||
return kVK_F20;
|
||||
case Qt::Key_F5:
|
||||
return kVK_F5;
|
||||
case Qt::Key_F6:
|
||||
return kVK_F6;
|
||||
case Qt::Key_F7:
|
||||
return kVK_F7;
|
||||
case Qt::Key_F3:
|
||||
return kVK_F3;
|
||||
case Qt::Key_F8:
|
||||
return kVK_F8;
|
||||
case Qt::Key_F9:
|
||||
return kVK_F9;
|
||||
case Qt::Key_F11:
|
||||
return kVK_F11;
|
||||
case Qt::Key_F13:
|
||||
return kVK_F13;
|
||||
case Qt::Key_F16:
|
||||
return kVK_F16;
|
||||
case Qt::Key_F14:
|
||||
return kVK_F14;
|
||||
case Qt::Key_F10:
|
||||
return kVK_F10;
|
||||
case Qt::Key_F12:
|
||||
return kVK_F12;
|
||||
case Qt::Key_F15:
|
||||
return kVK_F15;
|
||||
case Qt::Key_Help:
|
||||
return kVK_Help;
|
||||
case Qt::Key_Home:
|
||||
return kVK_Home;
|
||||
case Qt::Key_PageUp:
|
||||
return kVK_PageUp;
|
||||
case Qt::Key_Delete:
|
||||
return kVK_ForwardDelete;
|
||||
case Qt::Key_F4:
|
||||
return kVK_F4;
|
||||
case Qt::Key_End:
|
||||
return kVK_End;
|
||||
case Qt::Key_F2:
|
||||
return kVK_F2;
|
||||
case Qt::Key_PageDown:
|
||||
return kVK_PageDown;
|
||||
case Qt::Key_F1:
|
||||
return kVK_F1;
|
||||
case Qt::Key_Left:
|
||||
return kVK_LeftArrow;
|
||||
case Qt::Key_Right:
|
||||
return kVK_RightArrow;
|
||||
case Qt::Key_Down:
|
||||
return kVK_DownArrow;
|
||||
case Qt::Key_Up:
|
||||
return kVK_UpArrow;
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
UTF16Char ch = keycode;
|
||||
|
||||
CFDataRef currentLayoutData;
|
||||
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
|
||||
|
||||
if (currentKeyboard == NULL)
|
||||
return 0;
|
||||
|
||||
currentLayoutData = (CFDataRef)TISGetInputSourceProperty(
|
||||
currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
|
||||
CFRelease(currentKeyboard);
|
||||
if (currentLayoutData == NULL)
|
||||
return 0;
|
||||
|
||||
UCKeyboardLayout* header =
|
||||
(UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
|
||||
UCKeyboardTypeHeader* table = header->keyboardTypeList;
|
||||
|
||||
uint8_t* data = (uint8_t*)header;
|
||||
for (quint32 i = 0; i < header->keyboardTypeCount; i++) {
|
||||
UCKeyStateRecordsIndex* stateRec = 0;
|
||||
if (table[i].keyStateRecordsIndexOffset != 0) {
|
||||
stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(
|
||||
data + table[i].keyStateRecordsIndexOffset);
|
||||
if (stateRec->keyStateRecordsIndexFormat !=
|
||||
kUCKeyStateRecordsIndexFormat)
|
||||
stateRec = 0;
|
||||
}
|
||||
|
||||
UCKeyToCharTableIndex* charTable =
|
||||
reinterpret_cast<UCKeyToCharTableIndex*>(
|
||||
data + table[i].keyToCharTableIndexOffset);
|
||||
if (charTable->keyToCharTableIndexFormat !=
|
||||
kUCKeyToCharTableIndexFormat)
|
||||
continue;
|
||||
|
||||
for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) {
|
||||
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(
|
||||
data + charTable->keyToCharTableOffsets[j]);
|
||||
for (quint32 k = 0; k < charTable->keyToCharTableSize; k++) {
|
||||
if (keyToChar[k] & kUCKeyOutputTestForIndexMask) {
|
||||
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
|
||||
if (stateRec && idx < stateRec->keyStateRecordCount) {
|
||||
UCKeyStateRecord* rec =
|
||||
reinterpret_cast<UCKeyStateRecord*>(
|
||||
data + stateRec->keyStateRecordOffsets[idx]);
|
||||
if (rec->stateZeroCharData == ch) {
|
||||
ok = true;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
} else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) &&
|
||||
keyToChar[k] < 0xFFFE) {
|
||||
if (keyToChar[k] == ch) {
|
||||
ok = true;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok)
|
||||
{
|
||||
quint32 nMods = 0;
|
||||
if (modifiers & Qt::ShiftModifier)
|
||||
nMods |= shiftKey;
|
||||
if (modifiers & Qt::ControlModifier)
|
||||
nMods |= cmdKey;
|
||||
if (modifiers & Qt::AltModifier)
|
||||
nMods |= optionKey;
|
||||
if (modifiers & Qt::MetaModifier)
|
||||
nMods |= controlKey;
|
||||
if (modifiers & Qt::KeypadModifier)
|
||||
nMods |= kEventKeyModifierNumLockMask;
|
||||
ok = true;
|
||||
return nMods;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
if (!this->isHotkeyHandlerRegistered) {
|
||||
EventTypeSpec pressEventSpec;
|
||||
pressEventSpec.eventClass = kEventClassKeyboard;
|
||||
pressEventSpec.eventKind = kEventHotKeyPressed;
|
||||
InstallApplicationEventHandler(
|
||||
&QHotkeyPrivateMac::hotkeyPressEventHandler,
|
||||
1,
|
||||
&pressEventSpec,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
EventTypeSpec releaseEventSpec;
|
||||
releaseEventSpec.eventClass = kEventClassKeyboard;
|
||||
releaseEventSpec.eventKind = kEventHotKeyReleased;
|
||||
InstallApplicationEventHandler(
|
||||
&QHotkeyPrivateMac::hotkeyReleaseEventHandler,
|
||||
1,
|
||||
&releaseEventSpec,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
EventHotKeyID hkeyID;
|
||||
hkeyID.signature = shortcut.key;
|
||||
hkeyID.id = shortcut.modifier;
|
||||
|
||||
EventHotKeyRef eventRef = 0;
|
||||
OSStatus status = RegisterEventHotKey(shortcut.key,
|
||||
shortcut.modifier,
|
||||
hkeyID,
|
||||
GetApplicationEventTarget(),
|
||||
0,
|
||||
&eventRef);
|
||||
if (status != noErr) {
|
||||
error = QString::number(status);
|
||||
return false;
|
||||
} else {
|
||||
this->hotkeyRefs.insert(shortcut, eventRef);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut);
|
||||
OSStatus status = UnregisterEventHotKey(eventRef);
|
||||
if (status != noErr) {
|
||||
error = QString::number(status);
|
||||
return false;
|
||||
} else {
|
||||
this->hotkeyRefs.remove(shortcut);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler(
|
||||
EventHandlerCallRef nextHandler,
|
||||
EventRef event,
|
||||
void* data)
|
||||
{
|
||||
Q_UNUSED(nextHandler);
|
||||
Q_UNUSED(data);
|
||||
|
||||
if (GetEventClass(event) == kEventClassKeyboard &&
|
||||
GetEventKind(event) == kEventHotKeyPressed) {
|
||||
EventHotKeyID hkeyID;
|
||||
GetEventParameter(event,
|
||||
kEventParamDirectObject,
|
||||
typeEventHotKeyID,
|
||||
NULL,
|
||||
sizeof(EventHotKeyID),
|
||||
NULL,
|
||||
&hkeyID);
|
||||
hotkeyPrivate->activateShortcut({ hkeyID.signature, hkeyID.id });
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler(
|
||||
EventHandlerCallRef nextHandler,
|
||||
EventRef event,
|
||||
void* data)
|
||||
{
|
||||
Q_UNUSED(nextHandler);
|
||||
Q_UNUSED(data);
|
||||
|
||||
if (GetEventClass(event) == kEventClassKeyboard &&
|
||||
GetEventKind(event) == kEventHotKeyReleased) {
|
||||
EventHotKeyID hkeyID;
|
||||
GetEventParameter(event,
|
||||
kEventParamDirectObject,
|
||||
typeEventHotKeyID,
|
||||
NULL,
|
||||
sizeof(EventHotKeyID),
|
||||
NULL,
|
||||
&hkeyID);
|
||||
hotkeyPrivate->releaseShortcut({ hkeyID.signature, hkeyID.id });
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
68
src/core/QHotkey/qhotkey_p.h
Normal file
68
src/core/QHotkey/qhotkey_p.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef QHOTKEY_P_H
|
||||
#define QHOTKEY_P_H
|
||||
|
||||
#include "qhotkey.h"
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QGlobalStatic>
|
||||
#include <QMultiHash>
|
||||
#include <QMutex>
|
||||
|
||||
class QHOTKEY_SHARED_EXPORT QHotkeyPrivate
|
||||
: public QObject
|
||||
, public QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHotkeyPrivate(); // singleton!!!
|
||||
~QHotkeyPrivate();
|
||||
|
||||
static QHotkeyPrivate* instance();
|
||||
static bool isPlatformSupported();
|
||||
|
||||
QHotkey::NativeShortcut nativeShortcut(Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers);
|
||||
|
||||
bool addShortcut(QHotkey* hotkey);
|
||||
bool removeShortcut(QHotkey* hotkey);
|
||||
|
||||
protected:
|
||||
void activateShortcut(QHotkey::NativeShortcut shortcut);
|
||||
void releaseShortcut(QHotkey::NativeShortcut shortcut);
|
||||
|
||||
virtual quint32 nativeKeycode(Qt::Key keycode,
|
||||
bool& ok) = 0; // platform implement
|
||||
virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok) = 0; // platform implement
|
||||
|
||||
virtual bool registerShortcut(
|
||||
QHotkey::NativeShortcut shortcut) = 0; // platform implement
|
||||
virtual bool unregisterShortcut(
|
||||
QHotkey::NativeShortcut shortcut) = 0; // platform implement
|
||||
|
||||
QString error;
|
||||
|
||||
private:
|
||||
QHash<QPair<Qt::Key, Qt::KeyboardModifiers>, QHotkey::NativeShortcut>
|
||||
mapping;
|
||||
QMultiHash<QHotkey::NativeShortcut, QHotkey*> shortcuts;
|
||||
|
||||
Q_INVOKABLE void addMappingInvoked(Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QHotkey::NativeShortcut nativeShortcut);
|
||||
Q_INVOKABLE bool addShortcutInvoked(QHotkey* hotkey);
|
||||
Q_INVOKABLE bool removeShortcutInvoked(QHotkey* hotkey);
|
||||
Q_INVOKABLE QHotkey::NativeShortcut nativeShortcutInvoked(
|
||||
Qt::Key keycode,
|
||||
Qt::KeyboardModifiers modifiers);
|
||||
};
|
||||
|
||||
#define NATIVE_INSTANCE(ClassName) \
|
||||
Q_GLOBAL_STATIC(ClassName, hotkeyPrivate) \
|
||||
\
|
||||
QHotkeyPrivate* QHotkeyPrivate::instance() { return hotkeyPrivate; }
|
||||
|
||||
Q_DECLARE_METATYPE(Qt::Key)
|
||||
Q_DECLARE_METATYPE(Qt::KeyboardModifiers)
|
||||
|
||||
#endif // QHOTKEY_P_H
|
||||
313
src/core/QHotkey/qhotkey_win.cpp
Normal file
313
src/core/QHotkey/qhotkey_win.cpp
Normal file
@@ -0,0 +1,313 @@
|
||||
#include "qhotkey.h"
|
||||
#include "qhotkey_p.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <qt_windows.h>
|
||||
|
||||
#define HKEY_ID(nativeShortcut) \
|
||||
(((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)
|
||||
|
||||
#if !defined(MOD_NOREPEAT)
|
||||
#define MOD_NOREPEAT 0x4000
|
||||
#endif
|
||||
|
||||
class QHotkeyPrivateWin : public QHotkeyPrivate
|
||||
{
|
||||
public:
|
||||
QHotkeyPrivateWin();
|
||||
// QAbstractNativeEventFilter interface
|
||||
bool nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result) Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void pollForHotkeyRelease();
|
||||
// QHotkeyPrivate interface
|
||||
quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE;
|
||||
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok) Q_DECL_OVERRIDE;
|
||||
bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
static QString formatWinError(DWORD winError);
|
||||
QTimer pollTimer;
|
||||
QHotkey::NativeShortcut polledShortcut;
|
||||
};
|
||||
NATIVE_INSTANCE(QHotkeyPrivateWin)
|
||||
|
||||
QHotkeyPrivateWin::QHotkeyPrivateWin()
|
||||
{
|
||||
pollTimer.setInterval(50);
|
||||
connect(&pollTimer,
|
||||
&QTimer::timeout,
|
||||
this,
|
||||
&QHotkeyPrivateWin::pollForHotkeyRelease);
|
||||
}
|
||||
|
||||
bool QHotkeyPrivate::isPlatformSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result)
|
||||
{
|
||||
Q_UNUSED(eventType)
|
||||
Q_UNUSED(result)
|
||||
|
||||
MSG* msg = static_cast<MSG*>(message);
|
||||
if (msg->message == WM_HOTKEY) {
|
||||
QHotkey::NativeShortcut shortcut = { HIWORD(msg->lParam),
|
||||
LOWORD(msg->lParam) };
|
||||
this->activateShortcut(shortcut);
|
||||
this->polledShortcut = shortcut;
|
||||
this->pollTimer.start();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QHotkeyPrivateWin::pollForHotkeyRelease()
|
||||
{
|
||||
bool pressed =
|
||||
(GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0;
|
||||
if (!pressed) {
|
||||
this->pollTimer.stop();
|
||||
this->releaseShortcut(this->polledShortcut);
|
||||
}
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool& ok)
|
||||
{
|
||||
ok = true;
|
||||
if (keycode <= 0xFFFF) { // Try to obtain the key from it's "character"
|
||||
const SHORT vKey = VkKeyScanW(static_cast<WCHAR>(keycode));
|
||||
if (vKey > -1)
|
||||
return LOBYTE(vKey);
|
||||
}
|
||||
|
||||
// find key from switch/case --> Only finds a very small subset of keys
|
||||
switch (keycode) {
|
||||
case Qt::Key_Escape:
|
||||
return VK_ESCAPE;
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
return VK_TAB;
|
||||
case Qt::Key_Backspace:
|
||||
return VK_BACK;
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
return VK_RETURN;
|
||||
case Qt::Key_Insert:
|
||||
return VK_INSERT;
|
||||
case Qt::Key_Delete:
|
||||
return VK_DELETE;
|
||||
case Qt::Key_Pause:
|
||||
return VK_PAUSE;
|
||||
case Qt::Key_Print:
|
||||
return VK_PRINT;
|
||||
case Qt::Key_Clear:
|
||||
return VK_CLEAR;
|
||||
case Qt::Key_Home:
|
||||
return VK_HOME;
|
||||
case Qt::Key_End:
|
||||
return VK_END;
|
||||
case Qt::Key_Left:
|
||||
return VK_LEFT;
|
||||
case Qt::Key_Up:
|
||||
return VK_UP;
|
||||
case Qt::Key_Right:
|
||||
return VK_RIGHT;
|
||||
case Qt::Key_Down:
|
||||
return VK_DOWN;
|
||||
case Qt::Key_PageUp:
|
||||
return VK_PRIOR;
|
||||
case Qt::Key_PageDown:
|
||||
return VK_NEXT;
|
||||
case Qt::Key_CapsLock:
|
||||
return VK_CAPITAL;
|
||||
case Qt::Key_NumLock:
|
||||
return VK_NUMLOCK;
|
||||
case Qt::Key_ScrollLock:
|
||||
return VK_SCROLL;
|
||||
|
||||
case Qt::Key_F1:
|
||||
return VK_F1;
|
||||
case Qt::Key_F2:
|
||||
return VK_F2;
|
||||
case Qt::Key_F3:
|
||||
return VK_F3;
|
||||
case Qt::Key_F4:
|
||||
return VK_F4;
|
||||
case Qt::Key_F5:
|
||||
return VK_F5;
|
||||
case Qt::Key_F6:
|
||||
return VK_F6;
|
||||
case Qt::Key_F7:
|
||||
return VK_F7;
|
||||
case Qt::Key_F8:
|
||||
return VK_F8;
|
||||
case Qt::Key_F9:
|
||||
return VK_F9;
|
||||
case Qt::Key_F10:
|
||||
return VK_F10;
|
||||
case Qt::Key_F11:
|
||||
return VK_F11;
|
||||
case Qt::Key_F12:
|
||||
return VK_F12;
|
||||
case Qt::Key_F13:
|
||||
return VK_F13;
|
||||
case Qt::Key_F14:
|
||||
return VK_F14;
|
||||
case Qt::Key_F15:
|
||||
return VK_F15;
|
||||
case Qt::Key_F16:
|
||||
return VK_F16;
|
||||
case Qt::Key_F17:
|
||||
return VK_F17;
|
||||
case Qt::Key_F18:
|
||||
return VK_F18;
|
||||
case Qt::Key_F19:
|
||||
return VK_F19;
|
||||
case Qt::Key_F20:
|
||||
return VK_F20;
|
||||
case Qt::Key_F21:
|
||||
return VK_F21;
|
||||
case Qt::Key_F22:
|
||||
return VK_F22;
|
||||
case Qt::Key_F23:
|
||||
return VK_F23;
|
||||
case Qt::Key_F24:
|
||||
return VK_F24;
|
||||
|
||||
case Qt::Key_Menu:
|
||||
return VK_APPS;
|
||||
case Qt::Key_Help:
|
||||
return VK_HELP;
|
||||
case Qt::Key_MediaNext:
|
||||
return VK_MEDIA_NEXT_TRACK;
|
||||
case Qt::Key_MediaPrevious:
|
||||
return VK_MEDIA_PREV_TRACK;
|
||||
case Qt::Key_MediaPlay:
|
||||
return VK_MEDIA_PLAY_PAUSE;
|
||||
case Qt::Key_MediaStop:
|
||||
return VK_MEDIA_STOP;
|
||||
case Qt::Key_VolumeDown:
|
||||
return VK_VOLUME_DOWN;
|
||||
case Qt::Key_VolumeUp:
|
||||
return VK_VOLUME_UP;
|
||||
case Qt::Key_VolumeMute:
|
||||
return VK_VOLUME_MUTE;
|
||||
case Qt::Key_Mode_switch:
|
||||
return VK_MODECHANGE;
|
||||
case Qt::Key_Select:
|
||||
return VK_SELECT;
|
||||
case Qt::Key_Printer:
|
||||
return VK_PRINT;
|
||||
case Qt::Key_Execute:
|
||||
return VK_EXECUTE;
|
||||
case Qt::Key_Sleep:
|
||||
return VK_SLEEP;
|
||||
case Qt::Key_Period:
|
||||
return VK_DECIMAL;
|
||||
case Qt::Key_Play:
|
||||
return VK_PLAY;
|
||||
case Qt::Key_Cancel:
|
||||
return VK_CANCEL;
|
||||
|
||||
case Qt::Key_Forward:
|
||||
return VK_BROWSER_FORWARD;
|
||||
case Qt::Key_Refresh:
|
||||
return VK_BROWSER_REFRESH;
|
||||
case Qt::Key_Stop:
|
||||
return VK_BROWSER_STOP;
|
||||
case Qt::Key_Search:
|
||||
return VK_BROWSER_SEARCH;
|
||||
case Qt::Key_Favorites:
|
||||
return VK_BROWSER_FAVORITES;
|
||||
case Qt::Key_HomePage:
|
||||
return VK_BROWSER_HOME;
|
||||
|
||||
case Qt::Key_LaunchMail:
|
||||
return VK_LAUNCH_MAIL;
|
||||
case Qt::Key_LaunchMedia:
|
||||
return VK_LAUNCH_MEDIA_SELECT;
|
||||
case Qt::Key_Launch0:
|
||||
return VK_LAUNCH_APP1;
|
||||
case Qt::Key_Launch1:
|
||||
return VK_LAUNCH_APP2;
|
||||
|
||||
case Qt::Key_Massyo:
|
||||
return VK_OEM_FJ_MASSHOU;
|
||||
case Qt::Key_Touroku:
|
||||
return VK_OEM_FJ_TOUROKU;
|
||||
|
||||
default:
|
||||
if (keycode <= 0xFFFF)
|
||||
return (byte)keycode;
|
||||
else {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok)
|
||||
{
|
||||
quint32 nMods = 0;
|
||||
if (modifiers & Qt::ShiftModifier)
|
||||
nMods |= MOD_SHIFT;
|
||||
if (modifiers & Qt::ControlModifier)
|
||||
nMods |= MOD_CONTROL;
|
||||
if (modifiers & Qt::AltModifier)
|
||||
nMods |= MOD_ALT;
|
||||
if (modifiers & Qt::MetaModifier)
|
||||
nMods |= MOD_WIN;
|
||||
ok = true;
|
||||
return nMods;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
BOOL ok = RegisterHotKey(
|
||||
NULL, HKEY_ID(shortcut), shortcut.modifier + MOD_NOREPEAT, shortcut.key);
|
||||
if (ok)
|
||||
return true;
|
||||
else {
|
||||
error = QHotkeyPrivateWin::formatWinError(::GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut));
|
||||
if (ok)
|
||||
return true;
|
||||
else {
|
||||
error = QHotkeyPrivateWin::formatWinError(::GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString QHotkeyPrivateWin::formatWinError(DWORD winError)
|
||||
{
|
||||
wchar_t* buffer = NULL;
|
||||
DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
winError,
|
||||
0,
|
||||
(LPWSTR)&buffer,
|
||||
0,
|
||||
NULL);
|
||||
if (buffer) {
|
||||
QString res = QString::fromWCharArray(buffer, num);
|
||||
LocalFree(buffer);
|
||||
return res;
|
||||
} else
|
||||
return QString();
|
||||
}
|
||||
259
src/core/QHotkey/qhotkey_x11.cpp
Normal file
259
src/core/QHotkey/qhotkey_x11.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "qhotkey.h"
|
||||
#include "qhotkey_p.h"
|
||||
#include <QDebug>
|
||||
#include <QThreadStorage>
|
||||
#include <QTimer>
|
||||
#include <QX11Info>
|
||||
#include <X11/Xlib.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
// compability to pre Qt 5.8
|
||||
#ifndef Q_FALLTHROUGH
|
||||
#define Q_FALLTHROUGH() (void)0
|
||||
#endif
|
||||
|
||||
class QHotkeyPrivateX11 : public QHotkeyPrivate
|
||||
{
|
||||
public:
|
||||
// QAbstractNativeEventFilter interface
|
||||
bool nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result) Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
// QHotkeyPrivate interface
|
||||
quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE;
|
||||
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok) Q_DECL_OVERRIDE;
|
||||
static QString getX11String(Qt::Key keycode);
|
||||
bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
static const QVector<quint32> specialModifiers;
|
||||
static const quint32 validModsMask;
|
||||
xcb_key_press_event_t prevHandledEvent;
|
||||
xcb_key_press_event_t prevEvent;
|
||||
|
||||
static QString formatX11Error(Display* display, int errorCode);
|
||||
|
||||
class HotkeyErrorHandler
|
||||
{
|
||||
public:
|
||||
HotkeyErrorHandler();
|
||||
~HotkeyErrorHandler();
|
||||
|
||||
static bool hasError;
|
||||
static QString errorString;
|
||||
|
||||
private:
|
||||
XErrorHandler prevHandler;
|
||||
|
||||
static int handleError(Display* display, XErrorEvent* error);
|
||||
};
|
||||
};
|
||||
NATIVE_INSTANCE(QHotkeyPrivateX11)
|
||||
|
||||
bool QHotkeyPrivate::isPlatformSupported()
|
||||
{
|
||||
return QX11Info::isPlatformX11();
|
||||
}
|
||||
|
||||
const QVector<quint32> QHotkeyPrivateX11::specialModifiers = { 0,
|
||||
Mod2Mask,
|
||||
LockMask,
|
||||
(Mod2Mask |
|
||||
LockMask) };
|
||||
const quint32 QHotkeyPrivateX11::validModsMask =
|
||||
ShiftMask | ControlMask | Mod1Mask | Mod4Mask;
|
||||
|
||||
bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray& eventType,
|
||||
void* message,
|
||||
long* result)
|
||||
{
|
||||
Q_UNUSED(eventType)
|
||||
Q_UNUSED(result)
|
||||
|
||||
auto* genericEvent = static_cast<xcb_generic_event_t*>(message);
|
||||
if (genericEvent->response_type == XCB_KEY_PRESS) {
|
||||
xcb_key_press_event_t keyEvent =
|
||||
*static_cast<xcb_key_press_event_t*>(message);
|
||||
this->prevEvent = keyEvent;
|
||||
if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) {
|
||||
if (this->prevHandledEvent.time == keyEvent.time)
|
||||
return false;
|
||||
}
|
||||
this->activateShortcut(
|
||||
{ keyEvent.detail,
|
||||
keyEvent.state & QHotkeyPrivateX11::validModsMask });
|
||||
} else if (genericEvent->response_type == XCB_KEY_RELEASE) {
|
||||
xcb_key_release_event_t keyEvent =
|
||||
*static_cast<xcb_key_release_event_t*>(message);
|
||||
this->prevEvent = keyEvent;
|
||||
QTimer::singleShot(50, [this, keyEvent] {
|
||||
if (this->prevEvent.time == keyEvent.time &&
|
||||
this->prevEvent.response_type == keyEvent.response_type &&
|
||||
this->prevEvent.detail == keyEvent.detail) {
|
||||
this->releaseShortcut(
|
||||
{ keyEvent.detail,
|
||||
keyEvent.state & QHotkeyPrivateX11::validModsMask });
|
||||
}
|
||||
});
|
||||
this->prevHandledEvent = keyEvent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString QHotkeyPrivateX11::getX11String(Qt::Key keycode)
|
||||
{
|
||||
switch (keycode) {
|
||||
|
||||
case Qt::Key_MediaLast:
|
||||
case Qt::Key_MediaPrevious:
|
||||
return QStringLiteral("XF86AudioPrev");
|
||||
case Qt::Key_MediaNext:
|
||||
return QStringLiteral("XF86AudioNext");
|
||||
case Qt::Key_MediaPause:
|
||||
case Qt::Key_MediaPlay:
|
||||
case Qt::Key_MediaTogglePlayPause:
|
||||
return QStringLiteral("XF86AudioPlay");
|
||||
case Qt::Key_MediaRecord:
|
||||
return QStringLiteral("XF86AudioRecord");
|
||||
case Qt::Key_MediaStop:
|
||||
return QStringLiteral("XF86AudioStop");
|
||||
default:
|
||||
return QKeySequence(keycode).toString(QKeySequence::NativeText);
|
||||
}
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool& ok)
|
||||
{
|
||||
QString keyString = getX11String(keycode);
|
||||
|
||||
KeySym keysym = XStringToKeysym(keyString.toLatin1().constData());
|
||||
if (keysym == NoSymbol) {
|
||||
// not found -> just use the key
|
||||
if (keycode <= 0xFFFF)
|
||||
keysym = keycode;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (QX11Info::isPlatformX11()) {
|
||||
auto res = XKeysymToKeycode(QX11Info::display(), keysym);
|
||||
if (res != 0)
|
||||
ok = true;
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers,
|
||||
bool& ok)
|
||||
{
|
||||
quint32 nMods = 0;
|
||||
if (modifiers & Qt::ShiftModifier)
|
||||
nMods |= ShiftMask;
|
||||
if (modifiers & Qt::ControlModifier)
|
||||
nMods |= ControlMask;
|
||||
if (modifiers & Qt::AltModifier)
|
||||
nMods |= Mod1Mask;
|
||||
if (modifiers & Qt::MetaModifier)
|
||||
nMods |= Mod4Mask;
|
||||
ok = true;
|
||||
return nMods;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
Display* display = QX11Info::display();
|
||||
if (!display || !QX11Info::isPlatformX11())
|
||||
return false;
|
||||
|
||||
HotkeyErrorHandler errorHandler;
|
||||
for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
|
||||
XGrabKey(display,
|
||||
shortcut.key,
|
||||
shortcut.modifier | specialMod,
|
||||
DefaultRootWindow(display),
|
||||
True,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync);
|
||||
}
|
||||
XSync(display, False);
|
||||
|
||||
if (errorHandler.hasError) {
|
||||
error = errorHandler.errorString;
|
||||
this->unregisterShortcut(shortcut);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut)
|
||||
{
|
||||
Display* display = QX11Info::display();
|
||||
if (!display)
|
||||
return false;
|
||||
|
||||
HotkeyErrorHandler errorHandler;
|
||||
for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
|
||||
XUngrabKey(display,
|
||||
shortcut.key,
|
||||
shortcut.modifier | specialMod,
|
||||
XDefaultRootWindow(display));
|
||||
}
|
||||
XSync(display, False);
|
||||
|
||||
if (HotkeyErrorHandler::hasError) {
|
||||
error = HotkeyErrorHandler::errorString;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QHotkeyPrivateX11::formatX11Error(Display* display, int errorCode)
|
||||
{
|
||||
char errStr[256];
|
||||
XGetErrorText(display, errorCode, errStr, 256);
|
||||
return QString::fromLatin1(errStr);
|
||||
}
|
||||
|
||||
// ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ----------
|
||||
|
||||
bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false;
|
||||
QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString;
|
||||
|
||||
QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler()
|
||||
{
|
||||
prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError);
|
||||
}
|
||||
|
||||
QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler()
|
||||
{
|
||||
XSetErrorHandler(prevHandler);
|
||||
hasError = false;
|
||||
errorString.clear();
|
||||
}
|
||||
|
||||
int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display* display,
|
||||
XErrorEvent* error)
|
||||
{
|
||||
switch (error->error_code) {
|
||||
case BadAccess:
|
||||
case BadValue:
|
||||
case BadWindow:
|
||||
if (error->request_code == 33 || // grab key
|
||||
error->request_code == 34) { // ungrab key
|
||||
hasError = true;
|
||||
errorString =
|
||||
QHotkeyPrivateX11::formatX11Error(display, error->error_code);
|
||||
return 1;
|
||||
}
|
||||
Q_FALLTHROUGH();
|
||||
// fall through
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "controller.h"
|
||||
#include "src/config/configwindow.h"
|
||||
#include "src/core/QHotkey/QHotkey"
|
||||
#include "src/utils/confighandler.h"
|
||||
#include "src/utils/history.h"
|
||||
#include "src/utils/screengrabber.h"
|
||||
@@ -30,22 +31,42 @@
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDesktopWidget>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMenu>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "src/core/globalshortcutfilter.h"
|
||||
#endif
|
||||
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
// Controller is the core component of Flameshot, creates the trayIcon and
|
||||
// launches the capture widget
|
||||
|
||||
Controller::Controller()
|
||||
: m_captureWindow(nullptr)
|
||||
, m_history(nullptr)
|
||||
, m_trayIconMenu(nullptr)
|
||||
, m_networkCheckUpdates(nullptr)
|
||||
, m_showCheckAppUpdateStatus(false)
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
, m_HotkeyScreenshotCapture(nullptr)
|
||||
, m_HotkeyScreenshotHistory(nullptr)
|
||||
#endif
|
||||
{
|
||||
m_history = nullptr;
|
||||
|
||||
m_appLatestVersion = QStringLiteral(APP_VERSION).replace("v", "");
|
||||
qApp->setQuitOnLastWindowClosed(false);
|
||||
|
||||
// set default shortcusts if not set yet
|
||||
@@ -68,11 +89,36 @@ Controller::Controller()
|
||||
|
||||
QString StyleSheet = CaptureButton::globalStyleSheet();
|
||||
qApp->setStyleSheet(StyleSheet);
|
||||
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Try to take a test screenshot, MacOS will request a "Screen Recording"
|
||||
// permissions on the first run. Otherwise it will be hidden under the
|
||||
// CaptureWidget
|
||||
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
|
||||
currentScreen->grabWindow(QApplication::desktop()->winId(), 0, 0, 1, 1);
|
||||
|
||||
// set global shortcuts for MacOS
|
||||
m_HotkeyScreenshotCapture =
|
||||
new QHotkey(QKeySequence("Ctrl+Alt+Shift+4"), true, this);
|
||||
QObject::connect(m_HotkeyScreenshotCapture,
|
||||
&QHotkey::activated,
|
||||
qApp,
|
||||
[&]() { this->startVisualCapture(); });
|
||||
m_HotkeyScreenshotHistory =
|
||||
new QHotkey(QKeySequence("Ctrl+Alt+Shift+H"), true, this);
|
||||
QObject::connect(m_HotkeyScreenshotHistory,
|
||||
&QHotkey::activated,
|
||||
qApp,
|
||||
[&]() { this->showRecentScreenshots(); });
|
||||
#endif
|
||||
getLatestAvailableVersion();
|
||||
}
|
||||
|
||||
Controller::~Controller()
|
||||
{
|
||||
delete m_history;
|
||||
delete m_trayIconMenu;
|
||||
}
|
||||
|
||||
Controller* Controller::getInstance()
|
||||
@@ -89,6 +135,67 @@ void Controller::enableExports()
|
||||
this, &Controller::captureFailed, this, &Controller::handleCaptureFailed);
|
||||
}
|
||||
|
||||
void Controller::getLatestAvailableVersion()
|
||||
{
|
||||
// This features is required for MacOS and Windows user and for Linux users
|
||||
// who installed Flameshot not from the repository.
|
||||
m_networkCheckUpdates = new QNetworkAccessManager(this);
|
||||
QNetworkRequest requestCheckUpdates(QUrl(FLAMESHOT_APP_VERSION_URL));
|
||||
connect(m_networkCheckUpdates,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&Controller::handleReplyCheckUpdates);
|
||||
m_networkCheckUpdates->get(requestCheckUpdates);
|
||||
|
||||
// check for updates each 24 hours
|
||||
doLater(1000 * 60 * 60 * 24, this, [this]() {
|
||||
this->getLatestAvailableVersion();
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::handleReplyCheckUpdates(QNetworkReply* reply)
|
||||
{
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
QJsonDocument response = QJsonDocument::fromJson(reply->readAll());
|
||||
QJsonObject json = response.object();
|
||||
m_appLatestVersion = json["tag_name"].toString().replace("v", "");
|
||||
if (QStringLiteral(APP_VERSION)
|
||||
.replace("v", "")
|
||||
.compare(m_appLatestVersion) < 0) {
|
||||
m_appLatestUrl = json["html_url"].toString();
|
||||
QString newVersion =
|
||||
tr("New version %1 is available").arg(m_appLatestVersion);
|
||||
m_appUpdates->setText(newVersion);
|
||||
if (m_showCheckAppUpdateStatus) {
|
||||
sendTrayNotification(newVersion, "Flameshot");
|
||||
QDesktopServices::openUrl(QUrl(m_appLatestUrl));
|
||||
}
|
||||
} else if (m_showCheckAppUpdateStatus) {
|
||||
sendTrayNotification(tr("You have the latest version"),
|
||||
"Flameshot");
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to get information about the latest version. "
|
||||
<< reply->errorString();
|
||||
if (m_showCheckAppUpdateStatus) {
|
||||
sendTrayNotification(
|
||||
tr("Failed to get information about the latest version."),
|
||||
"Flameshot");
|
||||
}
|
||||
}
|
||||
m_showCheckAppUpdateStatus = false;
|
||||
}
|
||||
|
||||
void Controller::appUpdates()
|
||||
{
|
||||
if (m_appLatestUrl.isEmpty()) {
|
||||
m_showCheckAppUpdateStatus = true;
|
||||
getLatestAvailableVersion();
|
||||
} else {
|
||||
QDesktopServices::openUrl(QUrl(m_appLatestUrl));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::requestCapture(const CaptureRequest& request)
|
||||
{
|
||||
uint id = request.id();
|
||||
@@ -150,10 +257,21 @@ void Controller::startVisualCapture(const uint id,
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
m_captureWindow->show();
|
||||
#elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// In "Emulate fullscreen mode"
|
||||
m_captureWindow->showFullScreen();
|
||||
m_captureWindow->activateWindow();
|
||||
m_captureWindow->raise();
|
||||
#else
|
||||
m_captureWindow->showFullScreen();
|
||||
// m_captureWindow->show(); // Debug
|
||||
#endif
|
||||
if (!m_appLatestUrl.isEmpty() &&
|
||||
ConfigHandler().ignoreUpdateToVersion().compare(
|
||||
m_appLatestVersion) < 0) {
|
||||
m_captureWindow->showAppUpdateNotification(m_appLatestVersion,
|
||||
m_appLatestUrl);
|
||||
}
|
||||
} else {
|
||||
emit captureFailed(id);
|
||||
}
|
||||
@@ -183,6 +301,11 @@ void Controller::openConfigWindow()
|
||||
if (!m_configWindow) {
|
||||
m_configWindow = new ConfigWindow();
|
||||
m_configWindow->show();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
m_configWindow->activateWindow();
|
||||
m_configWindow->raise();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +314,11 @@ void Controller::openInfoWindow()
|
||||
{
|
||||
if (!m_infoWindow) {
|
||||
m_infoWindow = new InfoWindow();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
m_infoWindow->activateWindow();
|
||||
m_infoWindow->raise();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +328,11 @@ void Controller::openLauncherWindow()
|
||||
m_launcherWindow = new CaptureLauncher();
|
||||
}
|
||||
m_launcherWindow->show();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
m_launcherWindow->activateWindow();
|
||||
m_launcherWindow->raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::enableTrayIcon()
|
||||
@@ -207,7 +340,7 @@ void Controller::enableTrayIcon()
|
||||
if (m_trayIcon) {
|
||||
return;
|
||||
}
|
||||
QMenu* trayIconMenu = new QMenu();
|
||||
m_trayIconMenu = new QMenu();
|
||||
|
||||
ConfigHandler().setDisabledTrayIcon(false);
|
||||
QAction* captureAction = new QAction(tr("&Take Screenshot"), this);
|
||||
@@ -225,6 +358,10 @@ void Controller::enableTrayIcon()
|
||||
configAction, &QAction::triggered, this, &Controller::openConfigWindow);
|
||||
QAction* infoAction = new QAction(tr("&About"), this);
|
||||
connect(infoAction, &QAction::triggered, this, &Controller::openInfoWindow);
|
||||
|
||||
m_appUpdates = new QAction(tr("Check for updates"), this);
|
||||
connect(m_appUpdates, &QAction::triggered, this, &Controller::appUpdates);
|
||||
|
||||
QAction* quitAction = new QAction(tr("&Quit"), this);
|
||||
connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
|
||||
|
||||
@@ -234,29 +371,60 @@ void Controller::enableTrayIcon()
|
||||
recentAction, SIGNAL(triggered()), this, SLOT(showRecentScreenshots()));
|
||||
|
||||
// generate menu
|
||||
trayIconMenu->addAction(captureAction);
|
||||
trayIconMenu->addAction(launcherAction);
|
||||
trayIconMenu->addSeparator();
|
||||
trayIconMenu->addAction(recentAction);
|
||||
trayIconMenu->addSeparator();
|
||||
trayIconMenu->addAction(configAction);
|
||||
trayIconMenu->addAction(infoAction);
|
||||
trayIconMenu->addSeparator();
|
||||
trayIconMenu->addAction(quitAction);
|
||||
m_trayIconMenu->addAction(captureAction);
|
||||
m_trayIconMenu->addAction(launcherAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(recentAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(configAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(m_appUpdates);
|
||||
m_trayIconMenu->addAction(infoAction);
|
||||
m_trayIconMenu->addSeparator();
|
||||
m_trayIconMenu->addAction(quitAction);
|
||||
|
||||
m_trayIcon = new QSystemTrayIcon();
|
||||
m_trayIcon->setToolTip(QStringLiteral("Flameshot"));
|
||||
m_trayIcon->setContextMenu(trayIconMenu);
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX)
|
||||
// Because of the following issues on MacOS "Catalina":
|
||||
// https://bugreports.qt.io/browse/QTBUG-86393
|
||||
// https://developer.apple.com/forums/thread/126072
|
||||
auto currentMacOsVersion = QOperatingSystemVersion::current();
|
||||
if (currentMacOsVersion >= currentMacOsVersion.MacOSBigSur) {
|
||||
m_trayIcon->setContextMenu(m_trayIconMenu);
|
||||
}
|
||||
#else
|
||||
m_trayIcon->setContextMenu(m_trayIconMenu);
|
||||
#endif
|
||||
QIcon trayicon =
|
||||
QIcon::fromTheme("flameshot-tray", QIcon(":img/app/flameshot.png"));
|
||||
m_trayIcon->setIcon(trayicon);
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX)
|
||||
if (currentMacOsVersion < currentMacOsVersion.MacOSBigSur) {
|
||||
// Because of the following issues on MacOS "Catalina":
|
||||
// https://bugreports.qt.io/browse/QTBUG-86393
|
||||
// https://developer.apple.com/forums/thread/126072
|
||||
auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) {
|
||||
if (m_trayIconMenu->isVisible()) {
|
||||
m_trayIconMenu->hide();
|
||||
} else {
|
||||
m_trayIconMenu->popup(QCursor::pos());
|
||||
}
|
||||
};
|
||||
connect(
|
||||
m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated);
|
||||
}
|
||||
#else
|
||||
auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) {
|
||||
if (r == QSystemTrayIcon::Trigger) {
|
||||
startVisualCapture();
|
||||
}
|
||||
};
|
||||
connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// Ensure proper removal of tray icon when program quits on Windows.
|
||||
@@ -310,6 +478,11 @@ void Controller::showRecentScreenshots()
|
||||
}
|
||||
m_history->loadHistory();
|
||||
m_history->show();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
m_history->activateWindow();
|
||||
m_history->raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::startFullscreenCapture(const uint id)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "src/core/capturerequest.h"
|
||||
#include <QMap>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QPointer>
|
||||
@@ -31,6 +32,12 @@ class InfoWindow;
|
||||
class QSystemTrayIcon;
|
||||
class CaptureLauncher;
|
||||
class HistoryWidget;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
class QHotkey;
|
||||
#endif
|
||||
using lambda = std::function<void(void)>;
|
||||
|
||||
class Controller : public QObject
|
||||
@@ -56,6 +63,7 @@ public slots:
|
||||
|
||||
void openConfigWindow();
|
||||
void openInfoWindow();
|
||||
void appUpdates();
|
||||
void openLauncherWindow();
|
||||
void enableTrayIcon();
|
||||
void disableTrayIcon();
|
||||
@@ -77,13 +85,22 @@ private slots:
|
||||
void handleCaptureTaken(uint id, QPixmap p, QRect selection);
|
||||
void handleCaptureFailed(uint id);
|
||||
|
||||
void handleReplyCheckUpdates(QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
Controller();
|
||||
void getLatestAvailableVersion();
|
||||
|
||||
// replace QTimer::singleShot introduced in Qt 5.4
|
||||
// the actual target Qt version is 5.3
|
||||
void doLater(int msec, QObject* receiver, lambda func);
|
||||
|
||||
// class members
|
||||
QAction* m_appUpdates;
|
||||
QString m_appLatestUrl;
|
||||
QString m_appLatestVersion;
|
||||
bool m_showCheckAppUpdateStatus;
|
||||
|
||||
QMap<uint, CaptureRequest> m_requestMap;
|
||||
QPointer<CaptureWidget> m_captureWindow;
|
||||
QPointer<InfoWindow> m_infoWindow;
|
||||
@@ -92,4 +109,12 @@ private:
|
||||
QPointer<QSystemTrayIcon> m_trayIcon;
|
||||
|
||||
HistoryWidget* m_history;
|
||||
QMenu* m_trayIconMenu;
|
||||
|
||||
QNetworkAccessManager* m_networkCheckUpdates;
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QHotkey* m_HotkeyScreenshotCapture;
|
||||
QHotkey* m_HotkeyScreenshotHistory;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -93,7 +93,8 @@ int main(int argc, char* argv[])
|
||||
app.setOrganizationName(QStringLiteral("flameshot"));
|
||||
|
||||
auto c = Controller::getInstance();
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX) || defined(Q_OS_WIN))
|
||||
new FlameshotDBusAdapter(c);
|
||||
QDBusConnection dbus = QDBusConnection::sessionBus();
|
||||
if (!dbus.isConnected()) {
|
||||
@@ -109,7 +110,8 @@ int main(int argc, char* argv[])
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX) || defined(Q_OS_WIN))
|
||||
/*--------------|
|
||||
* CLI parsing |
|
||||
* ------------*/
|
||||
|
||||
@@ -36,9 +36,6 @@ public:
|
||||
void paintMousePreview(QPainter& painter,
|
||||
const CaptureContext& context) override;
|
||||
|
||||
protected:
|
||||
virtual ToolType nameID() const = 0;
|
||||
|
||||
public slots:
|
||||
void drawEnd(const QPoint& p) override;
|
||||
void drawMove(const QPoint& p) override;
|
||||
|
||||
@@ -46,9 +46,16 @@ bool AbstractPathTool::showMousePreview() const
|
||||
void AbstractPathTool::undo(QPixmap& pixmap)
|
||||
{
|
||||
QPainter p(&pixmap);
|
||||
const int val = m_thickness + m_padding;
|
||||
QRect area = m_backupArea + QMargins(val, val, val, val);
|
||||
p.drawPixmap(area.intersected(pixmap.rect()).topLeft(), m_pixmapBackup);
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Not sure how will it work on 4k and fullHd on Linux or Windows with a
|
||||
// capture of different displays with different DPI, so let it be MacOS
|
||||
// specific only.
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup);
|
||||
#else
|
||||
p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AbstractPathTool::drawEnd(const QPoint& p)
|
||||
@@ -73,9 +80,30 @@ void AbstractPathTool::thicknessChanged(const int th)
|
||||
|
||||
void AbstractPathTool::updateBackup(const QPixmap& pixmap)
|
||||
{
|
||||
m_pixmapBackup = pixmap.copy(backupRect(pixmap));
|
||||
}
|
||||
|
||||
QRect AbstractPathTool::backupRect(const QPixmap& pixmap) const
|
||||
{
|
||||
const QRect& limits = pixmap.rect();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Not sure how will it work on 4k and fullHd on Linux or Windows with a
|
||||
// capture of different displays with different DPI, so let it be MacOS
|
||||
// specific only.
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
const int val = (m_thickness + m_padding) * pixelRatio;
|
||||
QRect r = m_backupArea.normalized();
|
||||
if (1 != pixelRatio) {
|
||||
r.moveTo(r.topLeft() * pixelRatio);
|
||||
r.setSize(r.size() * pixelRatio);
|
||||
}
|
||||
#else
|
||||
const int val = m_thickness + m_padding;
|
||||
QRect area = m_backupArea.normalized() + QMargins(val, val, val, val);
|
||||
m_pixmapBackup = pixmap.copy(area);
|
||||
QRect r = m_backupArea.normalized();
|
||||
#endif
|
||||
r += QMargins(val, val, val, val);
|
||||
return r.intersected(limits);
|
||||
}
|
||||
|
||||
void AbstractPathTool::addPoint(const QPoint& point)
|
||||
|
||||
@@ -41,8 +41,7 @@ public slots:
|
||||
protected:
|
||||
void updateBackup(const QPixmap& pixmap);
|
||||
void addPoint(const QPoint& point);
|
||||
|
||||
virtual ToolType nameID() const = 0;
|
||||
QRect backupRect(const QPixmap& pixmap) const;
|
||||
|
||||
QPixmap m_pixmapBackup;
|
||||
QRect m_backupArea;
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
// along with Flameshot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "abstracttwopointtool.h"
|
||||
#include <QCursor>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
@@ -71,7 +74,16 @@ bool AbstractTwoPointTool::showMousePreview() const
|
||||
void AbstractTwoPointTool::undo(QPixmap& pixmap)
|
||||
{
|
||||
QPainter p(&pixmap);
|
||||
p.drawPixmap(backupRect(pixmap.rect()).topLeft(), m_pixmapBackup);
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Not sure how will it work on 4k and fullHd on Linux or Windows with a
|
||||
// capture of different displays with different DPI, so let it be MacOS
|
||||
// specific only.
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup);
|
||||
#else
|
||||
p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup);
|
||||
#endif
|
||||
if (this->nameID() == ToolType::CIRCLECOUNT) {
|
||||
emit requestAction(REQ_DECREMENT_CIRCLE_COUNT);
|
||||
}
|
||||
@@ -104,13 +116,27 @@ void AbstractTwoPointTool::thicknessChanged(const int th)
|
||||
|
||||
void AbstractTwoPointTool::updateBackup(const QPixmap& pixmap)
|
||||
{
|
||||
m_pixmapBackup = pixmap.copy(backupRect(pixmap.rect()));
|
||||
m_pixmapBackup = pixmap.copy(backupRect(pixmap));
|
||||
}
|
||||
|
||||
QRect AbstractTwoPointTool::backupRect(const QRect& limits) const
|
||||
QRect AbstractTwoPointTool::backupRect(const QPixmap& pixmap) const
|
||||
{
|
||||
const QRect& limits = pixmap.rect();
|
||||
QRect r = QRect(m_points.first, m_points.second).normalized();
|
||||
const int val = m_thickness + m_padding;
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Not sure how will it work on 4k and fullHd on Linux or Windows with a
|
||||
// capture of different displays with different DPI, so let it be MacOS
|
||||
// specific only.
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
if (1 != pixelRatio) {
|
||||
r.moveTo(r.topLeft() * pixelRatio);
|
||||
r.setSize(r.size() * pixelRatio);
|
||||
}
|
||||
const int val = (m_thickness + m_padding) * pixelRatio;
|
||||
#else
|
||||
const int val = (m_thickness + m_padding);
|
||||
#endif
|
||||
r += QMargins(val, val, val, val);
|
||||
return r.intersected(limits);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public slots:
|
||||
|
||||
protected:
|
||||
void updateBackup(const QPixmap& pixmap);
|
||||
QRect backupRect(const QRect& limits) const;
|
||||
QRect backupRect(const QPixmap& pixmap) const;
|
||||
|
||||
QPixmap m_pixmapBackup;
|
||||
QPair<QPoint, QPoint> m_points;
|
||||
@@ -53,8 +53,6 @@ protected:
|
||||
bool m_supportsOrthogonalAdj = false;
|
||||
bool m_supportsDiagonalAdj = false;
|
||||
|
||||
virtual ToolType nameID() const = 0;
|
||||
|
||||
private:
|
||||
QPoint adjustedVector(QPoint v) const;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ class CopyTool : public AbstractActionTool
|
||||
public:
|
||||
explicit CopyTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class ExitTool : public AbstractActionTool
|
||||
public:
|
||||
explicit ExitTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class AppLauncher : public AbstractActionTool
|
||||
public:
|
||||
explicit AppLauncher(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class MoveTool : public AbstractActionTool
|
||||
public:
|
||||
explicit MoveTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class PinTool : public AbstractActionTool
|
||||
public:
|
||||
explicit PinTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <QGraphicsScene>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <cassert>
|
||||
|
||||
PixelateTool::PixelateTool(QObject* parent)
|
||||
: AbstractTwoPointTool(parent)
|
||||
@@ -65,13 +64,12 @@ void PixelateTool::process(QPainter& painter,
|
||||
QPoint& p0 = m_points.first;
|
||||
QPoint& p1 = m_points.second;
|
||||
QRect selection = QRect(p0, p1).normalized();
|
||||
auto pixelRatio = pixmap.devicePixelRatio();
|
||||
QRect selectionScaled =
|
||||
QRect(p0 * pixelRatio, p1 * pixelRatio).normalized();
|
||||
|
||||
// If thickness is less than 1, use old blur process
|
||||
if (m_thickness <= 1) {
|
||||
auto pixelRatio = pixmap.devicePixelRatio();
|
||||
|
||||
QRect selectionScaled =
|
||||
QRect(p0 * pixelRatio, p1 * pixelRatio).normalized();
|
||||
|
||||
QGraphicsBlurEffect* blur = new QGraphicsBlurEffect;
|
||||
blur->setBlurRadius(10);
|
||||
@@ -92,7 +90,7 @@ void PixelateTool::process(QPainter& painter,
|
||||
int height = selection.height() * (0.5 / qMax(1, m_thickness));
|
||||
QSize size = QSize(qMax(width, 1), qMax(height, 1));
|
||||
|
||||
QPixmap t = pixmap.copy(selection);
|
||||
QPixmap t = pixmap.copy(selectionScaled);
|
||||
t = t.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
t = t.scaled(selection.width(), selection.height());
|
||||
painter.drawImage(selection, t.toImage());
|
||||
|
||||
@@ -25,7 +25,7 @@ class RedoTool : public AbstractActionTool
|
||||
public:
|
||||
explicit RedoTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#include "savetool.h"
|
||||
#include "src/utils/screenshotsaver.h"
|
||||
#include <QPainter>
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
#include "src/widgets/capture/capturewidget.h"
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#endif
|
||||
|
||||
SaveTool::SaveTool(QObject* parent)
|
||||
: AbstractActionTool(parent)
|
||||
@@ -55,6 +61,17 @@ CaptureTool* SaveTool::copy(QObject* parent)
|
||||
|
||||
void SaveTool::pressed(const CaptureContext& context)
|
||||
{
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
for (QWidget* widget : qApp->topLevelWidgets()) {
|
||||
QString className(widget->metaObject()->className());
|
||||
if (0 ==
|
||||
className.compare(CaptureWidget::staticMetaObject.className())) {
|
||||
widget->showNormal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (context.savePath.isEmpty()) {
|
||||
emit requestAction(REQ_HIDE_GUI);
|
||||
bool ok = ScreenshotSaver().saveToFilesystemGUI(
|
||||
|
||||
@@ -25,7 +25,7 @@ class SaveTool : public AbstractActionTool
|
||||
public:
|
||||
explicit SaveTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class SelectionTool : public AbstractTwoPointTool
|
||||
public:
|
||||
explicit SelectionTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@ class SizeIndicatorTool : public AbstractActionTool
|
||||
public:
|
||||
explicit SizeIndicatorTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -130,7 +130,16 @@ CaptureTool* TextTool::copy(QObject* parent)
|
||||
void TextTool::undo(QPixmap& pixmap)
|
||||
{
|
||||
QPainter p(&pixmap);
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Not sure how will it work on 4k and fullHd on Linux or Windows with a
|
||||
// capture of different displays with different DPI, so let it be MacOS
|
||||
// specific only.
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup);
|
||||
#else
|
||||
p.drawPixmap(m_backupArea.topLeft(), m_pixmapBackup);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextTool::process(QPainter& painter,
|
||||
@@ -144,12 +153,32 @@ void TextTool::process(QPainter& painter,
|
||||
QSize size(fm.boundingRect(QRect(), 0, m_text).size());
|
||||
m_backupArea.setSize(size);
|
||||
if (recordUndo) {
|
||||
m_pixmapBackup = pixmap.copy(m_backupArea + QMargins(0, 0, 5, 5));
|
||||
m_pixmapBackup = pixmap.copy(backupRect(pixmap));
|
||||
}
|
||||
// draw text
|
||||
painter.setFont(m_font);
|
||||
painter.setPen(m_color);
|
||||
painter.drawText(m_backupArea + QMargins(-5, -5, 5, 5), m_text);
|
||||
const int val = 5;
|
||||
painter.drawText(m_backupArea + QMargins(-val, -val, val, val), m_text);
|
||||
}
|
||||
|
||||
QRect TextTool::backupRect(const QPixmap& pixmap) const
|
||||
{
|
||||
const QRect& limits = pixmap.rect();
|
||||
QRect r = m_backupArea.normalized();
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
const qreal pixelRatio = pixmap.devicePixelRatio();
|
||||
const int val = 5 * pixelRatio;
|
||||
if (1 != pixelRatio) {
|
||||
r.moveTo(r.topLeft() * pixelRatio);
|
||||
r.setSize(r.size() * pixelRatio);
|
||||
}
|
||||
#else
|
||||
const int val = 5;
|
||||
#endif
|
||||
r += QMargins(0, 0, val, val);
|
||||
return r.intersected(limits);
|
||||
}
|
||||
|
||||
void TextTool::paintMousePreview(QPainter& painter,
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
|
||||
protected:
|
||||
ToolType nameID() const override;
|
||||
QRect backupRect(const QPixmap& pixmap) const;
|
||||
|
||||
public slots:
|
||||
void drawEnd(const QPoint& p) override;
|
||||
|
||||
@@ -92,9 +92,12 @@ CaptureTool* ToolFactory::CreateTool(CaptureToolButton::ButtonType t,
|
||||
case CaptureToolButton::TYPE_REDO:
|
||||
tool = new RedoTool(parent);
|
||||
break;
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
case CaptureToolButton::TYPE_OPEN_APP:
|
||||
tool = new AppLauncher(parent);
|
||||
break;
|
||||
#endif
|
||||
case CaptureToolButton::TYPE_PIXELATE:
|
||||
tool = new PixelateTool(parent);
|
||||
break;
|
||||
@@ -107,7 +110,6 @@ CaptureTool* ToolFactory::CreateTool(CaptureToolButton::ButtonType t,
|
||||
case CaptureToolButton::TYPE_CIRCLECOUNT:
|
||||
tool = new CircleCountTool(parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
tool = nullptr;
|
||||
break;
|
||||
|
||||
@@ -25,7 +25,7 @@ class UndoTool : public AbstractActionTool
|
||||
public:
|
||||
explicit UndoTool(QObject* parent = nullptr);
|
||||
|
||||
bool closeOnButtonPressed() const;
|
||||
bool closeOnButtonPressed() const override;
|
||||
|
||||
QIcon icon(const QColor& background, bool inEditor) const override;
|
||||
QString name() const override;
|
||||
|
||||
@@ -60,7 +60,10 @@ QVector<CaptureToolButton::ButtonType> ConfigHandler::getButtons()
|
||||
<< CaptureToolButton::TYPE_COPY << CaptureToolButton::TYPE_SAVE
|
||||
<< CaptureToolButton::TYPE_EXIT
|
||||
<< CaptureToolButton::TYPE_IMAGEUPLOADER
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
<< CaptureToolButton::TYPE_OPEN_APP
|
||||
#endif
|
||||
<< CaptureToolButton::TYPE_PIN << CaptureToolButton::TYPE_TEXT
|
||||
<< CaptureToolButton::TYPE_CIRCLECOUNT;
|
||||
}
|
||||
@@ -230,6 +233,16 @@ void ConfigHandler::setShowSidePanelButton(const bool showSidePanelButton)
|
||||
showSidePanelButton);
|
||||
}
|
||||
|
||||
void ConfigHandler::setIgnoreUpdateToVersion(const QString& text)
|
||||
{
|
||||
m_settings.setValue(QStringLiteral("ignoreUpdateToVersion"), text);
|
||||
}
|
||||
|
||||
QString ConfigHandler::ignoreUpdateToVersion()
|
||||
{
|
||||
return m_settings.value(QStringLiteral("ignoreUpdateToVersion")).toString();
|
||||
}
|
||||
|
||||
bool ConfigHandler::desktopNotificationValue()
|
||||
{
|
||||
bool res = true;
|
||||
@@ -559,3 +572,28 @@ const QString& ConfigHandler::shortcut(const QString& shortcutName)
|
||||
m_settings.endGroup();
|
||||
return m_strRes;
|
||||
}
|
||||
|
||||
void ConfigHandler::setValue(const QString& group,
|
||||
const QString& key,
|
||||
const QVariant& value)
|
||||
{
|
||||
if (!group.isEmpty()) {
|
||||
m_settings.beginGroup(group);
|
||||
}
|
||||
m_settings.setValue(key, value);
|
||||
if (!group.isEmpty()) {
|
||||
m_settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant& ConfigHandler::value(const QString& group, const QString& key)
|
||||
{
|
||||
if (!group.isEmpty()) {
|
||||
m_settings.beginGroup(group);
|
||||
}
|
||||
m_varRes = m_settings.value(key);
|
||||
if (!group.isEmpty()) {
|
||||
m_settings.endGroup();
|
||||
}
|
||||
return m_varRes;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "src/widgets/capture/capturetoolbutton.h"
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
class ConfigHandler
|
||||
@@ -88,6 +89,9 @@ public:
|
||||
void setDefaults();
|
||||
void setAllTheButtons();
|
||||
|
||||
void setIgnoreUpdateToVersion(const QString& text);
|
||||
QString ignoreUpdateToVersion();
|
||||
|
||||
QVector<QStringList> shortcuts();
|
||||
void setShortcutsDefault();
|
||||
bool setShortcut(const QString&, const QString&);
|
||||
@@ -95,8 +99,14 @@ public:
|
||||
|
||||
QString configFilePath() const;
|
||||
|
||||
void setValue(const QString& group,
|
||||
const QString& key,
|
||||
const QVariant& value);
|
||||
QVariant& value(const QString& group, const QString& key);
|
||||
|
||||
private:
|
||||
QString m_strRes;
|
||||
QVariant m_varRes;
|
||||
QSettings m_settings;
|
||||
QVector<QStringList> m_shortcuts;
|
||||
|
||||
|
||||
@@ -58,10 +58,18 @@ const QVector<QStringList>& ConfigShortcuts::captureShortcutsDefault(
|
||||
|
||||
m_shortcuts << (QStringList() << "" << QObject::tr("Quit capture")
|
||||
<< QKeySequence(Qt::Key_Escape).toString());
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
m_shortcuts << (QStringList()
|
||||
<< "" << QObject::tr("Screenshot history") << "⇧⌘⌥H");
|
||||
m_shortcuts << (QStringList()
|
||||
<< "" << QObject::tr("Capture screen") << "⇧⌘⌥4");
|
||||
#else
|
||||
m_shortcuts << (QStringList() << "" << QObject::tr("Screenshot history")
|
||||
<< "Shift+Print Screen");
|
||||
m_shortcuts << (QStringList()
|
||||
<< "" << QObject::tr("Capture screen") << "Print Screen");
|
||||
#endif
|
||||
m_shortcuts << (QStringList()
|
||||
<< "" << QObject::tr("Show color picker") << "Right Click");
|
||||
m_shortcuts << (QStringList()
|
||||
@@ -116,9 +124,12 @@ const QKeySequence& ConfigShortcuts::captureShortcutDefault(
|
||||
case CaptureToolButton::ButtonType::TYPE_IMAGEUPLOADER:
|
||||
m_ks = QKeySequence(Qt::Key_Return);
|
||||
break;
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
case CaptureToolButton::ButtonType::TYPE_OPEN_APP:
|
||||
m_ks = QKeySequence(Qt::CTRL + Qt::Key_O);
|
||||
break;
|
||||
#endif
|
||||
case CaptureToolButton::ButtonType::TYPE_PIXELATE:
|
||||
m_ks = QKeySequence(Qt::Key_B);
|
||||
break;
|
||||
|
||||
@@ -30,9 +30,20 @@ const QString& History::path()
|
||||
|
||||
void History::save(const QPixmap& pixmap, const QString& fileName)
|
||||
{
|
||||
// scale preview only in local disk
|
||||
QPixmap pixmapScaled = QPixmap(pixmap);
|
||||
if (pixmap.height() / HISTORYPIXMAP_MAX_PREVIEW_HEIGHT >=
|
||||
pixmap.width() / HISTORYPIXMAP_MAX_PREVIEW_WIDTH) {
|
||||
pixmapScaled = pixmap.scaledToHeight(HISTORYPIXMAP_MAX_PREVIEW_HEIGHT);
|
||||
} else {
|
||||
pixmapScaled = pixmap.scaledToWidth(HISTORYPIXMAP_MAX_PREVIEW_WIDTH);
|
||||
}
|
||||
|
||||
// save preview
|
||||
QFile file(path() + fileName);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
pixmap.save(&file, "PNG");
|
||||
pixmapScaled.save(&file, "PNG");
|
||||
|
||||
history();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#define HISTORY_MAX_SIZE 25
|
||||
|
||||
#define HISTORYPIXMAP_MAX_PREVIEW_WIDTH 160
|
||||
#define HISTORYPIXMAP_MAX_PREVIEW_HEIGHT 90
|
||||
|
||||
#include <QList>
|
||||
#include <QPixmap>
|
||||
#include <QString>
|
||||
|
||||
@@ -37,7 +37,18 @@ ScreenGrabber::ScreenGrabber(QObject* parent)
|
||||
QPixmap ScreenGrabber::grabEntireDesktop(bool& ok)
|
||||
{
|
||||
ok = true;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
|
||||
QPixmap screenPixmap(
|
||||
currentScreen->grabWindow(QApplication::desktop()->winId(),
|
||||
currentScreen->geometry().x(),
|
||||
currentScreen->geometry().y(),
|
||||
currentScreen->geometry().width(),
|
||||
currentScreen->geometry().height()));
|
||||
screenPixmap.setDevicePixelRatio(currentScreen->devicePixelRatio());
|
||||
return screenPixmap;
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
|
||||
if (m_info.waylandDectected()) {
|
||||
QPixmap res;
|
||||
// handle screenshot based on DE
|
||||
@@ -87,7 +98,7 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok)
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) || defined(Q_OS_WIN)
|
||||
QRect geometry;
|
||||
for (QScreen* const screen : QGuiApplication::screens()) {
|
||||
QRect scrRect = screen->geometry();
|
||||
@@ -106,6 +117,7 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok)
|
||||
QScreen* screen = QApplication::screens()[screenNumber];
|
||||
p.setDevicePixelRatio(screen->devicePixelRatio());
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
QPixmap ScreenGrabber::grabScreen(int screenNumber, bool& ok)
|
||||
|
||||
@@ -35,9 +35,9 @@ void ScreenshotSaver::saveToClipboard(const QPixmap& capture)
|
||||
// If we are able to properly save the file, save the file and copy to
|
||||
// clipboard.
|
||||
if ((ConfigHandler().saveAfterCopyValue()) &&
|
||||
(!ConfigHandler().saveAfterCopyPathValue().isEmpty())) {
|
||||
(!ConfigHandler().savePath().isEmpty())) {
|
||||
saveToFilesystem(capture,
|
||||
ConfigHandler().saveAfterCopyPathValue(),
|
||||
ConfigHandler().savePath(),
|
||||
QObject::tr("Capture saved to clipboard."));
|
||||
QApplication::clipboard()->setPixmap(capture);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
#include "systemnotification.h"
|
||||
#include "src/core/controller.h"
|
||||
#include "src/utils/confighandler.h"
|
||||
#include <QApplication>
|
||||
#include <QUrl>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX) || defined(Q_OS_WIN))
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusMessage>
|
||||
#else
|
||||
#endif
|
||||
#include "src/core/controller.h"
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
|
||||
SystemNotification::SystemNotification(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_interface(nullptr)
|
||||
{
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX) || defined(Q_OS_WIN))
|
||||
m_interface =
|
||||
new QDBusInterface(QStringLiteral("org.freedesktop.Notifications"),
|
||||
QStringLiteral("/org/freedesktop/Notifications"),
|
||||
QStringLiteral("org.freedesktop.Notifications"),
|
||||
QDBusConnection::sessionBus(),
|
||||
this);
|
||||
}
|
||||
#else
|
||||
SystemNotification::SystemNotification(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_interface = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SystemNotification::sendMessage(const QString& text,
|
||||
const QString& savePath)
|
||||
@@ -45,7 +41,10 @@ void SystemNotification::sendMessage(const QString& text,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX) || defined(Q_OS_WIN)
|
||||
Controller::getInstance()->sendTrayNotification(text, title, timeout);
|
||||
#else
|
||||
QList<QVariant> args;
|
||||
QVariantMap hintsMap;
|
||||
if (!savePath.isEmpty()) {
|
||||
@@ -64,8 +63,5 @@ void SystemNotification::sendMessage(const QString& text,
|
||||
<< timeout; // timeout
|
||||
m_interface->callWithArgumentList(
|
||||
QDBus::AutoDetect, QStringLiteral("Notify"), args);
|
||||
#else
|
||||
auto c = Controller::getInstance();
|
||||
c->sendTrayNotification(text, title, timeout);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ target_sources(
|
||||
notificationwidget.h
|
||||
orientablepushbutton.h
|
||||
historywidget.h
|
||||
updatenotificationwidget.h
|
||||
)
|
||||
|
||||
target_sources(
|
||||
@@ -24,4 +25,5 @@ target_sources(
|
||||
notificationwidget.cpp
|
||||
orientablepushbutton.cpp
|
||||
historywidget.cpp
|
||||
updatenotificationwidget.cpp
|
||||
)
|
||||
|
||||
@@ -115,27 +115,32 @@ void CaptureToolButton::setColor(const QColor& c)
|
||||
|
||||
QColor CaptureToolButton::m_mainColor = ConfigHandler().uiMainColorValue();
|
||||
|
||||
static std::map<CaptureToolButton::ButtonType, int> buttonTypeOrder{
|
||||
static std::map<CaptureToolButton::ButtonType, int> buttonTypeOrder
|
||||
{
|
||||
{ CaptureToolButton::TYPE_PENCIL, 0 },
|
||||
{ CaptureToolButton::TYPE_DRAWER, 1 },
|
||||
{ CaptureToolButton::TYPE_ARROW, 2 },
|
||||
{ CaptureToolButton::TYPE_SELECTION, 3 },
|
||||
{ CaptureToolButton::TYPE_RECTANGLE, 4 },
|
||||
{ CaptureToolButton::TYPE_CIRCLE, 5 },
|
||||
{ CaptureToolButton::TYPE_MARKER, 6 },
|
||||
{ CaptureToolButton::TYPE_TEXT, 7 },
|
||||
{ CaptureToolButton::TYPE_PIXELATE, 8 },
|
||||
{ CaptureToolButton::TYPE_CIRCLECOUNT, 9 },
|
||||
{ CaptureToolButton::TYPE_SELECTIONINDICATOR, 10 },
|
||||
{ CaptureToolButton::TYPE_MOVESELECTION, 11 },
|
||||
{ CaptureToolButton::TYPE_UNDO, 12 },
|
||||
{ CaptureToolButton::TYPE_REDO, 13 },
|
||||
{ CaptureToolButton::TYPE_COPY, 14 },
|
||||
{ CaptureToolButton::TYPE_SAVE, 15 },
|
||||
{ CaptureToolButton::TYPE_EXIT, 16 },
|
||||
{ CaptureToolButton::TYPE_IMAGEUPLOADER, 17 },
|
||||
{ CaptureToolButton::TYPE_OPEN_APP, 18 },
|
||||
{ CaptureToolButton::TYPE_PIN, 19 },
|
||||
{ CaptureToolButton::TYPE_DRAWER, 1 },
|
||||
{ CaptureToolButton::TYPE_ARROW, 2 },
|
||||
{ CaptureToolButton::TYPE_SELECTION, 3 },
|
||||
{ CaptureToolButton::TYPE_RECTANGLE, 4 },
|
||||
{ CaptureToolButton::TYPE_CIRCLE, 5 },
|
||||
{ CaptureToolButton::TYPE_MARKER, 6 },
|
||||
{ CaptureToolButton::TYPE_TEXT, 7 },
|
||||
{ CaptureToolButton::TYPE_PIXELATE, 8 },
|
||||
{ CaptureToolButton::TYPE_CIRCLECOUNT, 9 },
|
||||
{ CaptureToolButton::TYPE_SELECTIONINDICATOR, 10 },
|
||||
{ CaptureToolButton::TYPE_MOVESELECTION, 11 },
|
||||
{ CaptureToolButton::TYPE_UNDO, 12 },
|
||||
{ CaptureToolButton::TYPE_REDO, 13 },
|
||||
{ CaptureToolButton::TYPE_COPY, 14 },
|
||||
{ CaptureToolButton::TYPE_SAVE, 15 },
|
||||
{ CaptureToolButton::TYPE_IMAGEUPLOADER, 16 },
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
{ CaptureToolButton::TYPE_OPEN_APP, 17 },
|
||||
{ CaptureToolButton::TYPE_EXIT, 18 }, { CaptureToolButton::TYPE_PIN, 19 },
|
||||
#else
|
||||
{ CaptureToolButton::TYPE_EXIT, 17 }, { CaptureToolButton::TYPE_PIN, 18 },
|
||||
#endif
|
||||
};
|
||||
|
||||
int CaptureToolButton::getPriorityByButton(CaptureToolButton::ButtonType b)
|
||||
@@ -164,7 +169,10 @@ QVector<CaptureToolButton::ButtonType>
|
||||
CaptureToolButton::TYPE_SAVE,
|
||||
CaptureToolButton::TYPE_EXIT,
|
||||
CaptureToolButton::TYPE_IMAGEUPLOADER,
|
||||
#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
CaptureToolButton::TYPE_OPEN_APP,
|
||||
#endif
|
||||
CaptureToolButton::TYPE_PIN,
|
||||
CaptureToolButton::TYPE_CIRCLECOUNT,
|
||||
};
|
||||
|
||||
@@ -53,8 +53,7 @@ public:
|
||||
TYPE_REDO = 16,
|
||||
TYPE_PIN = 17,
|
||||
TYPE_TEXT = 18,
|
||||
TYPE_CIRCLECOUNT = 19,
|
||||
|
||||
TYPE_CIRCLECOUNT = 19
|
||||
};
|
||||
Q_ENUM(ButtonType)
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
#include "src/widgets/capture/notifierbox.h"
|
||||
#include "src/widgets/orientablepushbutton.h"
|
||||
#include "src/widgets/panel/sidepanelwidget.h"
|
||||
#include "src/widgets/updatenotificationwidget.h"
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDesktopWidget>
|
||||
#include <QGuiApplication>
|
||||
#include <QPaintEvent>
|
||||
@@ -70,6 +72,8 @@ CaptureWidget::CaptureWidget(const uint id,
|
||||
, m_toolWidget(nullptr)
|
||||
, m_mouseOverHandle(SelectionWidget::NO_SIDE)
|
||||
, m_id(id)
|
||||
, m_lastMouseWheel(0)
|
||||
, m_updateNotificationWidget(nullptr)
|
||||
{
|
||||
// Base config of the widget
|
||||
m_eventFilter = new HoverEventFilter(this);
|
||||
@@ -88,7 +92,8 @@ CaptureWidget::CaptureWidget(const uint id,
|
||||
initContext(savePath, fullScreen);
|
||||
initShortcuts();
|
||||
m_context.circleCount = 1;
|
||||
#ifdef Q_OS_WIN
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \
|
||||
defined(Q_OS_MACOS) || defined(Q_OS_MACX))
|
||||
// Top left of the whole set of screens
|
||||
QPoint topLeft(0, 0);
|
||||
#endif
|
||||
@@ -102,7 +107,7 @@ CaptureWidget::CaptureWidget(const uint id,
|
||||
}
|
||||
m_context.origScreenshot = m_context.screenshot;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if defined(Q_OS_WIN)
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
|
||||
Qt::Popup);
|
||||
|
||||
@@ -117,11 +122,24 @@ CaptureWidget::CaptureWidget(const uint id,
|
||||
}
|
||||
}
|
||||
move(topLeft);
|
||||
resize(pixmap().size());
|
||||
#elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
// Emulate fullscreen mode
|
||||
// setWindowFlags(Qt::WindowStaysOnTopHint |
|
||||
// Qt::BypassWindowManagerHint |
|
||||
// Qt::FramelessWindowHint |
|
||||
// Qt::NoDropShadowWindowHint | Qt::ToolTip |
|
||||
// Qt::Popup
|
||||
// );
|
||||
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
|
||||
move(currentScreen->geometry().x(), currentScreen->geometry().y());
|
||||
resize(currentScreen->size());
|
||||
#else
|
||||
setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint |
|
||||
Qt::FramelessWindowHint | Qt::Tool);
|
||||
#endif
|
||||
resize(pixmap().size());
|
||||
#endif
|
||||
}
|
||||
// Create buttons
|
||||
m_buttonHandler = new ButtonHandler(this);
|
||||
@@ -132,7 +150,7 @@ CaptureWidget::CaptureWidget(const uint id,
|
||||
QRect r = screen->geometry();
|
||||
r.moveTo(r.x() / screen->devicePixelRatio(),
|
||||
r.y() / screen->devicePixelRatio());
|
||||
#ifdef Q_OS_WIN
|
||||
#if defined(Q_OS_WIN)
|
||||
r.moveTo(r.topLeft() - topLeft);
|
||||
#endif
|
||||
areas.append(r);
|
||||
@@ -304,7 +322,19 @@ void CaptureWidget::paintEvent(QPaintEvent*)
|
||||
painter.setClipRect(rect());
|
||||
|
||||
if (m_showInitialMsg) {
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QRect helpRect;
|
||||
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
|
||||
if (currentScreen) {
|
||||
helpRect = currentScreen->geometry();
|
||||
} else {
|
||||
helpRect = QGuiApplication::primaryScreen()->geometry();
|
||||
}
|
||||
#else
|
||||
QRect helpRect = QGuiApplication::primaryScreen()->geometry();
|
||||
#endif
|
||||
|
||||
helpRect.moveTo(mapFromGlobal(helpRect.topLeft()));
|
||||
|
||||
QString helpTxt =
|
||||
@@ -606,7 +636,36 @@ void CaptureWidget::keyReleaseEvent(QKeyEvent* e)
|
||||
|
||||
void CaptureWidget::wheelEvent(QWheelEvent* e)
|
||||
{
|
||||
m_context.thickness += e->angleDelta().y() / 120;
|
||||
/* Mouse scroll usually gives value 120, not more or less, just how many
|
||||
* times.
|
||||
* Touchpad gives the value 2 or more (usually 2-8), it doesn't give
|
||||
* too big values like mouse wheel on normal scrolling, so it is almost
|
||||
* impossible to scroll. It's easier to calculate number of requests and do
|
||||
* not accept events faster that one in 200ms.
|
||||
* */
|
||||
int thicknessOffset = 0;
|
||||
if (e->angleDelta().y() >= 60) {
|
||||
// mouse scroll (wheel) increment
|
||||
thicknessOffset = 1;
|
||||
} else if (e->angleDelta().y() <= -60) {
|
||||
// mouse scroll (wheel) decrement
|
||||
thicknessOffset = -1;
|
||||
} else {
|
||||
// touchpad scroll
|
||||
qint64 current = QDateTime::currentMSecsSinceEpoch();
|
||||
if ((current - m_lastMouseWheel) > 200) {
|
||||
if (e->angleDelta().y() > 0) {
|
||||
thicknessOffset = 1;
|
||||
} else if (e->angleDelta().y() < 0) {
|
||||
thicknessOffset = -1;
|
||||
}
|
||||
m_lastMouseWheel = current;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_context.thickness += thicknessOffset;
|
||||
m_context.thickness = qBound(0, m_context.thickness, 100);
|
||||
QPoint topLeft =
|
||||
qApp->desktop()
|
||||
@@ -681,10 +740,19 @@ void CaptureWidget::initPanel()
|
||||
}
|
||||
|
||||
m_panel = new UtilityPanel(this);
|
||||
m_panel->hide();
|
||||
makeChild(m_panel);
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
|
||||
panelRect.moveTo(mapFromGlobal(panelRect.topLeft()));
|
||||
m_panel->setFixedWidth(m_colorPicker->width() * 1.5);
|
||||
m_panel->setFixedHeight(currentScreen->geometry().height());
|
||||
#else
|
||||
panelRect.moveTo(mapFromGlobal(panelRect.topLeft()));
|
||||
panelRect.setWidth(m_colorPicker->width() * 1.5);
|
||||
m_panel->setGeometry(panelRect);
|
||||
#endif
|
||||
|
||||
SidePanelWidget* sidePanel = new SidePanelWidget(&m_context.screenshot);
|
||||
connect(sidePanel,
|
||||
@@ -711,6 +779,26 @@ void CaptureWidget::initPanel()
|
||||
m_panel->pushWidget(new QUndoView(&m_undoStack, this));
|
||||
}
|
||||
|
||||
void CaptureWidget::showAppUpdateNotification(const QString& appLatestVersion,
|
||||
const QString& appLatestUrl)
|
||||
{
|
||||
if (nullptr == m_updateNotificationWidget) {
|
||||
m_updateNotificationWidget =
|
||||
new UpdateNotificationWidget(this, appLatestVersion, appLatestUrl);
|
||||
}
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
int ax = (width() - m_updateNotificationWidget->width()) / 2;
|
||||
#else
|
||||
QRect helpRect = QGuiApplication::primaryScreen()->geometry();
|
||||
int ax = helpRect.left() +
|
||||
((helpRect.width() - m_updateNotificationWidget->width()) / 2);
|
||||
#endif
|
||||
m_updateNotificationWidget->move(ax, 0);
|
||||
makeChild(m_updateNotificationWidget);
|
||||
m_updateNotificationWidget->show();
|
||||
}
|
||||
|
||||
void CaptureWidget::initSelection()
|
||||
{
|
||||
m_selection = new SelectionWidget(m_uiColor, this);
|
||||
@@ -1101,6 +1189,10 @@ void CaptureWidget::copyScreenshot()
|
||||
|
||||
void CaptureWidget::saveScreenshot()
|
||||
{
|
||||
#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \
|
||||
defined(Q_OS_MACX))
|
||||
showNormal();
|
||||
#endif
|
||||
m_captureDone = true;
|
||||
if (m_activeTool != nullptr) {
|
||||
QPainter painter(&m_context.screenshot);
|
||||
|
||||
@@ -44,6 +44,7 @@ class QNetworkReply;
|
||||
class ColorPicker;
|
||||
class NotifierBox;
|
||||
class HoverEventFilter;
|
||||
class UpdateNotificationWidget;
|
||||
|
||||
class CaptureWidget : public QWidget
|
||||
{
|
||||
@@ -58,6 +59,8 @@ public:
|
||||
|
||||
void updateButtons();
|
||||
QPixmap pixmap();
|
||||
void showAppUpdateNotification(const QString& appLatestVersion,
|
||||
const QString& appLatestUrl);
|
||||
|
||||
public slots:
|
||||
bool commitCurrentTool();
|
||||
@@ -149,6 +152,8 @@ private:
|
||||
QRect extendedRect(QRect* r) const;
|
||||
|
||||
private:
|
||||
UpdateNotificationWidget* m_updateNotificationWidget;
|
||||
quint64 m_lastMouseWheel;
|
||||
QUndoStack m_undoStack;
|
||||
QPointer<CaptureToolButton> m_sizeIndButton;
|
||||
// Last pressed button
|
||||
|
||||
@@ -73,6 +73,8 @@ bool DraggableWidgetMaker::eventFilter(QObject* obj, QEvent* event)
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
|
||||
@@ -100,6 +100,8 @@ void HistoryWidget::addLine(const QString& path, const QString& fileName)
|
||||
QPixmap pixmap;
|
||||
pixmap.load(fullFileName, "png");
|
||||
|
||||
// TODO - remove much later, it is still required to keep old previews works
|
||||
// fine
|
||||
if (pixmap.height() / HISTORYPIXMAP_MAX_PREVIEW_HEIGHT >=
|
||||
pixmap.width() / HISTORYPIXMAP_MAX_PREVIEW_WIDTH) {
|
||||
pixmap = pixmap.scaledToHeight(HISTORYPIXMAP_MAX_PREVIEW_HEIGHT);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#ifndef HISTORYWIDGET_H
|
||||
#define HISTORYWIDGET_H
|
||||
|
||||
#define HISTORYPIXMAP_MAX_PREVIEW_WIDTH 160
|
||||
#define HISTORYPIXMAP_MAX_PREVIEW_HEIGHT 90
|
||||
|
||||
#include <QDialog>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
@@ -43,7 +43,10 @@ UtilityPanel::UtilityPanel(QWidget* parent)
|
||||
m_internalPanel,
|
||||
&QWidget::hide);
|
||||
|
||||
hide();
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \
|
||||
defined(Q_OS_MACOS) || defined(Q_OS_MACX))
|
||||
move(0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
QWidget* UtilityPanel::toolWidget() const
|
||||
@@ -83,6 +86,10 @@ void UtilityPanel::show()
|
||||
m_showAnimation->setEndValue(QRect(0, 0, width(), height()));
|
||||
m_internalPanel->show();
|
||||
m_showAnimation->start();
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \
|
||||
defined(Q_OS_MACOS) || defined(Q_OS_MACX))
|
||||
move(0, 0);
|
||||
#endif
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
@@ -134,9 +141,5 @@ void UtilityPanel::initInternalPanel()
|
||||
m_hide->setText(tr("Hide"));
|
||||
m_upLayout->addWidget(m_hide);
|
||||
connect(m_hide, SIGNAL(clicked()), this, SLOT(slotHidePanel()));
|
||||
}
|
||||
|
||||
void UtilityPanel::slotHidePanel()
|
||||
{
|
||||
hide();
|
||||
m_internalPanel->hide();
|
||||
}
|
||||
|
||||
@@ -38,13 +38,8 @@ public:
|
||||
void hide();
|
||||
void show();
|
||||
|
||||
signals:
|
||||
void mouseEnter();
|
||||
void mouseLeave();
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
void slotHidePanel();
|
||||
|
||||
private:
|
||||
void initInternalPanel();
|
||||
|
||||
144
src/widgets/updatenotificationwidget.cpp
Normal file
144
src/widgets/updatenotificationwidget.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// Created by yuriypuchkov on 09.12.2020.
|
||||
//
|
||||
|
||||
#include "updatenotificationwidget.h"
|
||||
#include "src/utils/confighandler.h"
|
||||
#include <QDesktopServices>
|
||||
#include <QLabel>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWheelEvent>
|
||||
|
||||
UpdateNotificationWidget::UpdateNotificationWidget(
|
||||
QWidget* parent,
|
||||
const QString& appLatestVersion,
|
||||
const QString& appLatestUrl)
|
||||
: QWidget(parent)
|
||||
, m_appLatestVersion(appLatestVersion)
|
||||
, m_appLatestUrl(appLatestUrl)
|
||||
, m_layout(nullptr)
|
||||
{
|
||||
setMinimumSize(400, 100);
|
||||
initInternalPanel();
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
setCursor(Qt::ArrowCursor);
|
||||
|
||||
m_showAnimation = new QPropertyAnimation(m_internalPanel, "geometry", this);
|
||||
m_showAnimation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
m_showAnimation->setDuration(300);
|
||||
|
||||
m_hideAnimation = new QPropertyAnimation(m_internalPanel, "geometry", this);
|
||||
m_hideAnimation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
m_hideAnimation->setDuration(300);
|
||||
|
||||
connect(m_hideAnimation,
|
||||
&QPropertyAnimation::finished,
|
||||
m_internalPanel,
|
||||
&QWidget::hide);
|
||||
setAppLatestVersion(appLatestVersion);
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::show()
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
m_showAnimation->setStartValue(QRect(0, -height(), width(), height()));
|
||||
m_showAnimation->setEndValue(QRect(0, 0, width(), height()));
|
||||
m_internalPanel->show();
|
||||
m_showAnimation->start();
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::hide()
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
m_hideAnimation->setStartValue(QRect(0, 0, width(), height()));
|
||||
m_hideAnimation->setEndValue(QRect(0, -height(), 0, height()));
|
||||
m_hideAnimation->start();
|
||||
m_internalPanel->hide();
|
||||
QWidget::hide();
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::setAppLatestVersion(const QString& latestVersion)
|
||||
{
|
||||
m_appLatestVersion = latestVersion;
|
||||
QString newVersion =
|
||||
tr("New Flameshot version %1 is available").arg(latestVersion);
|
||||
m_notification->setText(newVersion);
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::laterButton()
|
||||
{
|
||||
hide();
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::ignoreButton()
|
||||
{
|
||||
ConfigHandler().setIgnoreUpdateToVersion(m_appLatestVersion);
|
||||
hide();
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::updateButton()
|
||||
{
|
||||
QDesktopServices::openUrl(m_appLatestUrl);
|
||||
hide();
|
||||
if (parentWidget()) {
|
||||
parentWidget()->close();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateNotificationWidget::initInternalPanel()
|
||||
{
|
||||
m_internalPanel = new QScrollArea(this);
|
||||
m_internalPanel->setAttribute(Qt::WA_NoMousePropagation);
|
||||
QWidget* widget = new QWidget();
|
||||
m_internalPanel->setWidget(widget);
|
||||
m_internalPanel->setWidgetResizable(true);
|
||||
|
||||
QColor bgColor = palette().window().color();
|
||||
bgColor.setAlphaF(0.0);
|
||||
m_internalPanel->setStyleSheet(
|
||||
QStringLiteral("QScrollArea {background-color: %1}").arg(bgColor.name()));
|
||||
m_internalPanel->hide();
|
||||
|
||||
//
|
||||
m_layout = new QVBoxLayout();
|
||||
widget->setLayout(m_layout);
|
||||
|
||||
// caption
|
||||
m_notification = new QLabel(m_appLatestVersion, this);
|
||||
m_layout->addWidget(m_notification);
|
||||
|
||||
// buttons layout
|
||||
QHBoxLayout* buttonsLayout = new QHBoxLayout();
|
||||
QSpacerItem* bottonsSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding);
|
||||
buttonsLayout->addSpacerItem(bottonsSpacer);
|
||||
m_layout->addLayout(buttonsLayout);
|
||||
|
||||
// ignore
|
||||
QPushButton* ignoreBtn = new QPushButton(tr("Ignore"), this);
|
||||
buttonsLayout->addWidget(ignoreBtn);
|
||||
connect(ignoreBtn,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&UpdateNotificationWidget::ignoreButton);
|
||||
|
||||
// later
|
||||
QPushButton* laterBtn = new QPushButton(tr("Later"), this);
|
||||
buttonsLayout->addWidget(laterBtn);
|
||||
connect(laterBtn,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&UpdateNotificationWidget::laterButton);
|
||||
|
||||
// update
|
||||
QPushButton* updateBtn = new QPushButton(tr("Update"), this);
|
||||
buttonsLayout->addWidget(updateBtn);
|
||||
connect(updateBtn,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&UpdateNotificationWidget::updateButton);
|
||||
}
|
||||
47
src/widgets/updatenotificationwidget.h
Normal file
47
src/widgets/updatenotificationwidget.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by yuriypuchkov on 09.12.2020.
|
||||
//
|
||||
|
||||
#ifndef FLAMESHOT_UPDATENOTIFICATIONWIDGET_H
|
||||
#define FLAMESHOT_UPDATENOTIFICATIONWIDGET_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
class QVBoxLayout;
|
||||
class QPropertyAnimation;
|
||||
class QScrollArea;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
|
||||
class UpdateNotificationWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateNotificationWidget(QWidget* parent,
|
||||
const QString& appLatestVersion,
|
||||
const QString& appLatestUrl);
|
||||
void setAppLatestVersion(const QString& latestVersion);
|
||||
|
||||
void hide();
|
||||
void show();
|
||||
|
||||
public slots:
|
||||
void ignoreButton();
|
||||
void laterButton();
|
||||
void updateButton();
|
||||
|
||||
private:
|
||||
void initInternalPanel();
|
||||
|
||||
// class members
|
||||
QString m_appLatestVersion;
|
||||
QString m_appLatestUrl;
|
||||
QVBoxLayout* m_layout;
|
||||
QLabel* m_notification;
|
||||
QScrollArea* m_internalPanel;
|
||||
QPropertyAnimation* m_showAnimation;
|
||||
QPropertyAnimation* m_hideAnimation;
|
||||
};
|
||||
|
||||
#endif // FLAMESHOT_UPDATENOTIFICATIONWIDGET_H
|
||||
Reference in New Issue
Block a user