From 177246133f86b08ab553bf479a13691295eef661 Mon Sep 17 00:00:00 2001 From: XMuli Date: Wed, 24 Aug 2022 01:27:44 +0800 Subject: [PATCH] refactor: Rewrite CMake --- .github/workflows/LinuxDeb.yml | 2 +- src/CMakeLists.txt | 216 +- src/platform/iwininfo.h | 178 +- src/preference/hotkeyswidget.cpp | 1 + src/screen/drawhelper.h | 603 +++--- src/screen/rectcalcu.cpp | 514 +++-- src/screen/screenshot.cpp | 3245 +++++++++++++++--------------- src/screen/screenshot.h | 314 ++- src/tool/base/colorparabar.h | 121 +- src/tool/base/managebar.h | 79 +- src/xglobal.h | 2 +- 11 files changed, 2561 insertions(+), 2714 deletions(-) diff --git a/.github/workflows/LinuxDeb.yml b/.github/workflows/LinuxDeb.yml index 8cefed0..a64308f 100644 --- a/.github/workflows/LinuxDeb.yml +++ b/.github/workflows/LinuxDeb.yml @@ -18,7 +18,7 @@ jobs: qt_ver: [5.15.2] # 参考: https://mirrors.cloud.tencent.com/qt/online/qtsdkrepository/linux_x64/desktop/qt5_5152 qt_target: [desktop] qt_arch: [gcc_64] - arch: [amd64, arm64] + arch: [amd64] # arm64 os: [ubuntu-20.04] env: targetName: PicShot diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 44dd88d..7b14f13 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,63 +7,53 @@ project(${PROJECT_NAME}) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) file(COPY config/config.ini DESTINATION ${CMAKE_BINARY_DIR}/bin) -# msvc multicore compilation and UNICODE -if(WIN32) - if(MSVC) - OPTION(USE_MP "use multiple" ON) - OPTION(ProjectConfig_Global_COMPILE_FLAGS_WITH_MP - "Set The Global Option COMPILE_FLAGS /MP to target." ON) - if(ProjectConfig_Global_COMPILE_FLAGS_WITH_MP OR USE_MP) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") - endif() - set(VS_STARTUP_PROJECT ${PROJECT_NAME}) - - add_compile_options("$<$:/utf-8>") - add_compile_options("$<$:/utf-8>") +# msvc multicore compilation +if(MSVC) + OPTION(USE_MP "use multiple" ON) + OPTION(ProjectConfig_Global_COMPILE_FLAGS_WITH_MP + "Set The Global Option COMPILE_FLAGS /MP to target." ON) + if(ProjectConfig_Global_COMPILE_FLAGS_WITH_MP OR USE_MP) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") endif() + + set(VS_STARTUP_PROJECT ${PROJECT_NAME}) + add_compile_options("$<$:/utf-8>") + add_compile_options("$<$:/utf-8>") endif() -# main -set(SRCS_MAIN +set(SRC_MAIN xglobal.h main.cpp) -# core -set(SRCS_CORE +set(SRC_CORE core/isingleton.h core/xlog.h) -# platform -set(SRCS_PLATFORM +set(SRC_PLATFORM platform/iwininfo.h platform/iwininfo.cpp platform/wininfo.h - platform/wininfo.cpp + platform/wininfo.cpp) - # 特定平台实现的文件列举在此,会自动剔除和分类归组。分类标准 *_win.* *_x11.* - platform/wininfo_win.h - platform/wininfo_win.cpp - platform/wininfo_x11.h - platform/wininfo_x11.cpp) - -# 正则 http://blog.icodeten.com/cmake/2015/01/22/cmake-experience/ -# 删除特定平台相关的 file -foreach(_rm_file ${SRCS_PLATFORM}) - string(REGEX MATCH ".*/*_win.h|.*/*_win.cpp|.*/*_x11.h|.*/*_x11.cpp" _need_remove_source ${_rm_file}) - if(_need_remove_source) - list(REMOVE_ITEM SRCS_PLATFORM ${_need_remove_source}) - endif(_need_remove_source) -endforeach(_rm_file) +if(APPLE) +elseif(WIN32) + list(APPEND SRC_PLATFORM + platform/wininfo_win.h + platform/wininfo_win.cpp) +else() + list(APPEND SRC_PLATFORM + platform/wininfo_x11.h + platform/wininfo_x11.cpp) +endif() -##----------- Test begin ----------- -#foreach(_rm_file ${SRCS_PLATFORM}) +#----------- Test begin ----------- +#foreach(_rm_file ${SRC_PLATFORM}) # message(${_rm_file}) -#endforeach(_rm_file) -##----------- Test end ----------- +#endforeach() +#----------- Test end ----------- -# custom widget -set(SRCS_WIDGET +set(SRC_WIDGET widget/xhorizontalline.h widget/xhorizontalline.cpp widget/xverticalline.h @@ -81,8 +71,7 @@ set(SRCS_WIDGET widget/xcombobox.h widget/xcombobox.cpp) -# tool -set(SRCS_TOOL +set(SRC_TOOL tool/base/colorparabar.h tool/base/colorparabar.cpp tool/base/managebar.h @@ -100,8 +89,7 @@ set(SRCS_TOOL tool/parameterbar.h tool/parameterbar.cpp) -# tool -set(SRCS_PERFERENCE +set(SRC_PERFERENCE preference/preference.h preference/preference.cpp preference/hotkeyswidget.h @@ -109,8 +97,7 @@ set(SRCS_PERFERENCE preference/appellation.h preference/appellation.cpp) -# screen -set(SRCS_WINSCREEN +set(SRC_WINSCREEN screen/drawhelper.h screen/drawhelper.cpp screen/rectcalcu.h @@ -120,19 +107,15 @@ set(SRCS_WINSCREEN screen/tray.h screen/tray.cpp) -# 测试模块 -set(SRCS_EXAMPLE +set(SRC_EXAMPLE example/exwidget.h example/exwidget.cpp) - -# 插件模块 -set(SRCS_PLUGIN_INTERFACE +set(SRC_PLUGIN_INTERFACE pluginsinterface/iplugininterface.h pluginsinterface/iplugininterface.cpp) -# 资源模块 -set(SRCS_RESOURCES +set(SRC_RESOURCES Resources.qrc logo.rc) @@ -162,7 +145,7 @@ find_package(Qt5 COMPONENTS REQUIRED) qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) # .ts transition .qm -message("#2---->" ${QM_FILES}) +# message("#2---->" ${QM_FILES}) #1. 自动批量生成 *.ts 文件 #2. 每次都能够更新 *ts 的增量 #3. 每次 清理 不会删除 .ts 文件 @@ -199,35 +182,34 @@ if (APPLE) # set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) add_executable(${PROJECT_NAME} MACOSX_BUNDLE - ${SRCS_MAIN} - ${SRCS_PLATFORM} - ${SRCS_CORE} - ${SRCS_WIDGET} - ${SRCS_TOOL} - ${SRCS_PERFERENCE} - ${SRCS_WINSCREEN} - ${SRCS_EXAMPLE} - ${SRCS_PLUGIN_INTERFACE} - ${SRCS_RESOURCES} + ${SRC_MAIN} + ${SRC_PLATFORM} + ${SRC_CORE} + ${SRC_WIDGET} + ${SRC_TOOL} + ${SRC_PERFERENCE} + ${SRC_WINSCREEN} + ${SRC_EXAMPLE} + ${SRC_PLUGIN_INTERFACE} + ${SRC_RESOURCES} #${QM_FILES} ) else() add_executable(${PROJECT_NAME} WIN32 - ${SRCS_MAIN} - ${SRCS_PLATFORM} - ${SRCS_CORE} - ${SRCS_WIDGET} - ${SRCS_TOOL} - ${SRCS_PERFERENCE} - ${SRCS_WINSCREEN} - ${SRCS_EXAMPLE} - ${SRCS_PLUGIN_INTERFACE} - ${SRCS_RESOURCES} + ${SRC_MAIN} + ${SRC_PLATFORM} + ${SRC_CORE} + ${SRC_WIDGET} + ${SRC_TOOL} + ${SRC_PERFERENCE} + ${SRC_WINSCREEN} + ${SRC_EXAMPLE} + ${SRC_PLUGIN_INTERFACE} + ${SRC_RESOURCES} #${QM_FILES} ) endif() - IF (WIN32) target_link_libraries(${PROJECT_NAME} ${QT5_LIBS_LINK} @@ -243,93 +225,27 @@ ELSEIF (UNIX) X11) ENDIF () -#target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog_header_only) - # 文件归类, ref: # https://stackoverflow.com/questions/33808087/cmake-how-to-create-visual-studio-filters # https://blog.csdn.net/gzj2013/article/details/102619480 -set(_src_root_path ${CMAKE_CURRENT_SOURCE_DIR}) # default root path curr path (CMakeList.txt) - +set(_src_root_path ${CMAKE_CURRENT_SOURCE_DIR}) # default root path curr path (CMakeList.txt) file(GLOB_RECURSE _source_list LIST_DIRECTORIES false "${_src_root_path}/*.cpp" "${_src_root_path}/*.h") -#message( "_source_list----------" ${_source_list}) - -source_group(TREE ${_src_root_path} FILES ${_source_list}) # [ source_group方法一, 会按照实际的目录结构进行组织, .h.cpp 放在一起] +source_group(TREE ${_src_root_path} FILES ${_source_list}) # will be organized according to the actual directory structure, .h.cpp is put together -# 特定平台的文件,进行过滤分组和添加得到编译中 -foreach(_source IN ITEMS ${_source_list}) - string(REGEX MATCH ".*/*_win.h|.*/*_win.cpp" _plat_win_source ${_source}) # 这个的匹配最后不能是 .c* 表示 .cpp - string(REGEX MATCH ".*/*_x11.h|.*/*_x11.cpp" _plat_x11_source ${_source}) - -# #----------- Test begin ----------- -# message( "_source -->" ${_source}) -# message( "_plat_win_source -->" ${_plat_win_source}) -# message( "_plat_x11_source -->" ${_plat_x11_source}) -# #----------- Test end ----------- - - get_filename_component(_source_path "${_source}" PATH) - file(RELATIVE_PATH _source_path_rel "${_src_root_path}" "${_source_path}") - string(REPLACE "/" "\\" _group_path "${_source_path_rel}") - - if(_plat_win_source OR _plat_x11_source) - if(APPLE) - elseif(WIN32) - target_sources(${PROJECT_NAME} PRIVATE "${_plat_win_source}") - source_group("${_group_path}" FILES "${_plat_win_source}") - else() - target_sources(${PROJECT_NAME} PRIVATE "${_plat_x11_source}") - source_group("${_group_path}" FILES "${_plat_x11_source}") - endif() - else() -# source_group("${_group_path}" FILES "${_source}") # [source_group 方法二, 自行分组 .h 和最后的 .cpp 是分开放在两个大分组下] - endif(_plat_win_source OR _plat_x11_source) - -endforeach() - -# https://www.ljjyy.com/archives/2021/03/100653.html -# CPU是32位还是64位 -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_bit_arch "x64") - message(STATUS "Target is 64 bits") -else() - set(_bit_arch "x86") - message(STATUS "Target is 32 bits") -endif() - -# 主机处理器架构 -if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i386") - message(STATUS "i386 architecture detected") -elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i686") - message(STATUS "i686 architecture detected") -elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") - message(STATUS "x86_64 architecture detected") -else() - message(STATUS "host processor architecture is unknown") -endif() - -## 此处配合 VMSVC 的 UTF8-BOM 插件,达到跨平台 -#if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") -# message("---using Clang---") -#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") -# message("---using GCC---") -#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") -# message("---using Intel C++---") -#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") -# message("---using Visual Studio C++---") -#endif() -# 定义一些变量,可以在 CPP 中使用 -target_compile_definitions(${PROJECT_NAME} PUBLIC _PROJECT_NAME="${PROJECT_NAME}") # PicShot -target_compile_definitions(${PROJECT_NAME} PUBLIC _PROJECT_VERSION="${_version}") # 0.2 这个没打印出来,很奇怪,改用下面这个 -target_compile_definitions(${PROJECT_NAME} PUBLIC _BIT_ARCH="${_bit_arch}") # x64/x86 +# Define some variables that can be used in *.cpp +target_compile_definitions(${PROJECT_NAME} PUBLIC _PROJECT_NAME="${PROJECT_NAME}") +target_compile_definitions(${PROJECT_NAME} PUBLIC _PROJECT_VERSION="${_version}") +target_compile_definitions(${PROJECT_NAME} PUBLIC _BIT_ARCH=${CMAKE_SIZEOF_VOID_P}) # x64/x86 target_compile_definitions(${PROJECT_NAME} PUBLIC _COMPILER=${CMAKE_HOST_SYSTEM_PROCESSOR}) # i386/i686/x86_64/unknown target_compile_definitions(${PROJECT_NAME} PUBLIC _COMPILER_ID="${CMAKE_CXX_COMPILER_ID}") # Clang/GCC/MSVC if(WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE - WIN32_LEAN_AND_MEAN # 仅仅包含常用的 API 的头文件,加快编译速度,效果明显 - _CRT_SECURE_NO_WARNINGS # 允许使用 strcpy,scanf等不安全的函数 + WIN32_LEAN_AND_MEAN # Header files containing only the common APIs + _CRT_SECURE_NO_WARNINGS # Unsafe functions such as strcpy, scanf, etc. are allowed UNICODE _UNICODE) endif() diff --git a/src/platform/iwininfo.h b/src/platform/iwininfo.h index 3c4e35b..4f9a6c3 100644 --- a/src/platform/iwininfo.h +++ b/src/platform/iwininfo.h @@ -1,92 +1,86 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2022.01.29 - * Description: - ******************************************************************/ -#ifndef IWININFO_H -#define IWININFO_H - -//#include "xtype.h" - - -#include -#include -#include -#include - -union WinID { - void* _hWnd; // NT OS - unsigned long _xWindow; // X11 OS -}; - -struct WinData -{ - WinData(WinID tId - , bool bTFilter - , QRect tRect - , QString tPath - , QString tName - , QString tTitle - , QString tReserved - , int tLevel - , int tCurIndex - , int tTotalIndex) - : id(tId) - , bFilter(bTFilter) - , rect(tRect) - , path(tPath) - , name(tName) - , title(tTitle) - , reserved(tReserved) - , level(tLevel) - , curIndex(tCurIndex) - , totalIndex(tTotalIndex){} - - WinData() - : id() // todo - , bFilter(false) - , rect(0, 0, 0, 0) - , path("") - , name("") - , title("") - , reserved("") - , level(0) - , curIndex(0) - , totalIndex(0) {} - - WinID id; - bool bFilter; - QRect rect; - QString path; - QString name; - QString title; - QString reserved; - int level; // 等级 - int curIndex; // 当前等级的序号 - int totalIndex; // 总的累计个数(横跨等级累计) -}; - -class IWinInfo -{ -public: - IWinInfo() = default; - virtual ~IWinInfo() = default; - - virtual void setWinIdFilter(WinID target) = 0; - virtual WinData* getWinInfoFromPoint(QPoint pt, bool bPrevCache = false) = 0; - -//protected: - - static std::vector m_vWinData; - static std::vector m_vWinIdFilter; -}; - - - -#endif // IWININFO_H - +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2022.01.29 + * Description: + ******************************************************************/ +#ifndef IWININFO_H +#define IWININFO_H + +#include +#include +#include +#include + +union WinID { + void* _hWnd; // NT OS + unsigned long _xWindow; // X11 OS +}; + +struct WinData +{ + WinData(WinID tId + , bool bTFilter + , QRect tRect + , QString tPath + , QString tName + , QString tTitle + , QString tReserved + , int tLevel + , int tCurIndex + , int tTotalIndex) + : id(tId) + , bFilter(bTFilter) + , rect(tRect) + , path(tPath) + , name(tName) + , title(tTitle) + , reserved(tReserved) + , level(tLevel) + , curIndex(tCurIndex) + , totalIndex(tTotalIndex){} + + WinData() + : id() // todo + , bFilter(false) + , rect(0, 0, 0, 0) + , path("") + , name("") + , title("") + , reserved("") + , level(0) + , curIndex(0) + , totalIndex(0) {} + + WinID id; + bool bFilter; + QRect rect; + QString path; + QString name; + QString title; + QString reserved; + int level; // 等级 + int curIndex; // 当前等级的序号 + int totalIndex; // 总的累计个数(横跨等级累计) +}; + +class IWinInfo +{ +public: + IWinInfo() = default; + virtual ~IWinInfo() = default; + + virtual void setWinIdFilter(WinID target) = 0; + virtual WinData* getWinInfoFromPoint(QPoint pt, bool bPrevCache = false) = 0; + +//protected: + static std::vector m_vWinData; + static std::vector m_vWinIdFilter; +}; + +#endif // IWININFO_H + diff --git a/src/preference/hotkeyswidget.cpp b/src/preference/hotkeyswidget.cpp index 350b240..c131b9e 100644 --- a/src/preference/hotkeyswidget.cpp +++ b/src/preference/hotkeyswidget.cpp @@ -18,6 +18,7 @@ #include #include #include +#include HotkeysWidget::HotkeysWidget(QWidget *parent) : QWidget(parent) { diff --git a/src/screen/drawhelper.h b/src/screen/drawhelper.h index fe04815..ae64eb7 100644 --- a/src/screen/drawhelper.h +++ b/src/screen/drawhelper.h @@ -1,306 +1,297 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2021.11.09 - * Description: - ******************************************************************/ -#ifndef XDRAW_H -#define XDRAW_H - -#include "../core/isingleton.h" -#include "../preference/appellation.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include - -class QPainterPath; -class QLine; -class QPoint; - - -class QDomElement; -namespace XC { - //enum ScrnStatus { - // // 基础的几种状态 - // SS_WaitBase, // 基础的等待状态(未有,和已有矩形局域) - // SS_SelectBase, // 基础的选中状态 - // SS_MoveBase, // 基础的移动状态 - // SS_DrawBase, // 基础的绘画状态 - // SS_StretchBase, // 基础的拉伸状态 - //}; - - //enum ScreenStatus { // ------------(矩形区域)------------ - // Auto, // 自动检测 - // Custom, // 手动拖曳 - - // Left, - // Right, - // Top, - // Bottom, - // TopLeft, - // TopRight, - // BottomLeft, - // BottomRight, - - // Width = Left | Right, // 左、右 - // Height = Top | Bottom, // 上、下 - // Edge = Width | Height, // 任意一边(左、右、上、下) - // TLAndBR = TopLeft | BottomRight, // 斜对角(左上、右下) - // TRAndBL = TopRight | BottomLeft, // 斜对角(右上、左下) - // Corner = TLAndBR | TRAndBL, // 斜任意一斜角(左上、右下、右上、左下) - // Frame = Edge | Corner, // 矩形任意一边或一斜角(左、右、上、下;左上、右下、右上、左下) - - // UnknowCursorType // 未知 - //}; - //Q_DECLARE_FLAGS(CursorTypes, ScreenStatus) // 枚举 ScrnType 生成宏 CursorTypes - // Q_DECLARE_OPERATORS_FOR_FLAGS(CursorTypes) // 重载宏 ScrnType 的 |() 函数 - - - - // C++11 新增带作用域的枚举,用 enum class 或enum struct(两者等价)声明。 - // https://blog.csdn.net/luckysym/article/details/1666114 - enum class DrawState { - Draw, - Move, - Delete, - Wateing, - Unknow - }; - - enum class DrawShape { - NoDraw, - Rectangles, - Ellipses, - LineWidth, // 特殊、线宽度 - Arrows, - Pen, - Mosaics, - Text, - SerialNumber - }; - - enum class ToolBarOffset { - TBO_Left, - TBO_Middle, - TBO_Right, - - TBO_Top = TBO_Left, - TBO_Bottom = TBO_Right - }; - - enum class LineEnds { - EmptyToEmpty, - EmptyToRecesedArrow, - DotToRecesedArrow, - EmptyToArrow, - DotToArrow, - EmptyToDot, - DotToEmpty, - DotToDot, - RecesedArrowToRecesedArrow, - ArrowToArrow - }; -} - -using namespace XC; - - -// NoDraw, -// Rectangles, -// Ellipses, -// Line, -// Arrows, -// Pen, -// Mosaics, -// Text, -// SerialNumber - -struct XDrawStep -{ - //new refactor - // base element ------------------ - QPoint p1; // 起点 - QPoint p2; // 终点 - QRect rt; // 初始绘画位置: 由 p1、p2 构成 - DrawShape shape = DrawShape::NoDraw; // 绘画形状 - int bStyele = 0; // refactor 图形的样式, 取代之前 bFill - int tbIdx = 0; // 某个绘画类型的样式: 为 ParameterBar 的子序号 - int idxLevel = -1; // 所处的序号等级,亦 z 轴,越大越顶层; 此项待重构 - static int totalIdx; // 所有已绘画类型 的总的序号,越大越上层 - - // color, pen width - QPen pen = QPen(Qt::red, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); // 默认红色,画笔宽度为 2px - QBrush brush = QBrush(Qt::NoBrush); // 画刷 - - // Rectangles -------------------- - // Ellipses ---------------------- - // Line / Arrows ----------------- - LineEnds lineEnds = LineEnds::EmptyToEmpty; - - // Arrows ------------------------ - // Pen --------------------------- - QVector custPath; // 手绘路径 - - // Mosaics ----------------------- - int mscPx = 3; // 马赛克默认模糊块 - - // Text -------------------------- - bool bDisplay = false; // true 准备显示,显示;false 显示结束,隐藏起来 - bool bTextComplete = true; // 文字编辑完成 - QPoint editPos; // 显示控件 - QString text = "[Test Text]"; // 文字内容 - QFont font; // 字体 - - // SerialNumber ------------------ - - void clear() { - p1 = QPoint(); - p2 = QPoint(); - rt = QRect(); - //shape = DrawShape::NoDraw; // 若为鼠标松开执行,则会无法继续绘画抽象图形 - //pen = QPen(Qt::red); - //penWidth = 2; - //brush = QBrush(Qt::NoBrush); - //brushWidth = 1; - //transparency = 1; - //rX = 8; - //rY = 8; - //lineEnd = LineEnds::EmptyToEmpty; - //lineDashe = LineDashes::SolidLine; - custPath.clear(); - //text = "==Test Text=="; - //font = QFont(); - //fontSize = 16; - //mscPx = 3; - - idxLevel = -1; - //g_index = -1; 永不重置 - - } -}; - -// ------------------------ - -class XHelper : public QObject -{ - Q_OBJECT -public: - static XHelper& instance() { - static XHelper instance; - return instance; - } - - XHelper(XHelper&&) = delete; - XHelper(const XHelper&) = delete; - void operator= (const XHelper&) = delete; -private: - XHelper(); - virtual ~XHelper() = default; - -public: - double getScale(QScreen* screen = QApplication::primaryScreen()); - - void setBoardStyle(const int index) { m_boardStyleIndex = index; } - const int boardStyle() { return m_boardStyleIndex; } - void setBorderColor(QColor color) { m_borderColor = color; } - const QColor borderColor() { return m_borderColor; } - void setBorderWidth(const int width) { m_borderWidth = width; } - const int borderWidth() { return m_borderWidth; } - void setCrosshairColor(QColor color) { m_crosshairColor = color; } - const QColor crosshairColor() { return m_crosshairColor; } - void setCrosshairWidth(const int width) { m_crosshairWidth = width; } - const int crosshairWidth() { return m_crosshairWidth; } - bool smartWindow() const { return m_enableSmartWindow; } - void setSmartWindow(bool enable) { m_enableSmartWindow = enable; } - bool crosshair() const { return m_enableCrosshair; } - void setCrosshair(bool enable) { m_enableCrosshair = enable; } - bool showCursor() const { return m_enableShowCursor; } - void setShowCursor(bool enable) { m_enableShowCursor = enable; } - bool autoCpoyClip() const { return m_enableAutoCpoyClip; } - void setAutoCpoyClip(bool enable) { m_enableAutoCpoyClip = enable; } - - int imgQuailty() const { return m_imgQuailty; } - void setImgQuailty(int val) { m_imgQuailty = val; } - const QString formatToName(const QString str = XHelper::instance().path(toFileName)); - - QIcon ChangeSVGColor(QString path, QString shape, QColor color, QSize size); - void SetAttrRecur(QDomElement& elem, QString strtagname, QString strattr, QString strattrval); - - // Mosaics draw - const QPixmap* SetMosaicSmooth(QPixmap* pixmap, int px); // 毛玻璃马赛克 - const QImage SetMosaicPixlelated(QPixmap* pixmap, int px = 20); // 像素级马赛克 - - // Arrow Line draw - QPainterPath GetArrowHead(QPoint p1, QPoint p2, const int thickness = 10); - QLine GetShorterLine(QPoint p1, QPoint p2, const int thickness = 10); - - // tabGeneral - // tabInterface - - // tabOutput - QString path(const QString key) const; - void setPath(const QString key, const QString val); - - // tabPin - bool winShadow() const; - void setWinShadow(bool enable); - - int pinOpacity() const; - void setPinOpacity(int opacity); - int pinMaxSize() const; - void setPinMaxSize(int val); - - -signals: - void sigChangeWinShadow(bool enable); - void sigChangeOpacity(int opacity); - void sigChangeMaxSize(int val); - - -private: - int m_boardStyleIndex; - QColor m_borderColor; // 边框 - int m_borderWidth; - QColor m_crosshairColor; // 边框 - int m_crosshairWidth; - bool m_enableSmartWindow; - bool m_enableCrosshair; - bool m_enableShowCursor; - bool m_enableAutoCpoyClip; - - int m_imgQuailty; - QMap m_path; - - bool m_winShadow; - int m_pinOpacity; - int m_pinMaxSize; -}; - -// ------------------------ -// 创建全局静态 单例 的对象, 就不浪费生命重新创建了, 路径后面替换为 ConfigLocation -Q_GLOBAL_STATIC_WITH_ARGS(QSettings, insSettings, (qApp->applicationDirPath() + "/config.ini", QSettings::IniFormat)); -//Q_GLOBAL_STATIC(XHelper, insXHelp); - - // perference UI config -const QString INIT_GENERAL("General"); // 初始化 常规 -const QString INIT_INTERFACE("Interface"); // 初始化 界面 -const QString INIT_OUTPUT("Output"); // 初始化 输出 -const QString INIT_PIN("Pin"); // 初始化 贴图 -const QString INIT_HOTKEYS("Hotkeys"); // 初始化 快捷键 - -#endif // XDRAW_H +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2021.11.09 + * Description: + ******************************************************************/ +#ifndef XDRAW_H +#define XDRAW_H + +#include "../core/isingleton.h" +#include "../preference/appellation.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +class QPainterPath; +class QLine; +class QPoint; +class QDomElement; + +namespace XC { + //enum ScrnStatus { + // // 基础的几种状态 + // SS_WaitBase, // 基础的等待状态(未有,和已有矩形局域) + // SS_SelectBase, // 基础的选中状态 + // SS_MoveBase, // 基础的移动状态 + // SS_DrawBase, // 基础的绘画状态 + // SS_StretchBase, // 基础的拉伸状态 + //}; + + //enum ScreenStatus { // ------------(矩形区域)------------ + // Auto, // 自动检测 + // Custom, // 手动拖曳 + + // Left, + // Right, + // Top, + // Bottom, + // TopLeft, + // TopRight, + // BottomLeft, + // BottomRight, + + // Width = Left | Right, // 左、右 + // Height = Top | Bottom, // 上、下 + // Edge = Width | Height, // 任意一边(左、右、上、下) + // TLAndBR = TopLeft | BottomRight, // 斜对角(左上、右下) + // TRAndBL = TopRight | BottomLeft, // 斜对角(右上、左下) + // Corner = TLAndBR | TRAndBL, // 斜任意一斜角(左上、右下、右上、左下) + // Frame = Edge | Corner, // 矩形任意一边或一斜角(左、右、上、下;左上、右下、右上、左下) + + // UnknowCursorType // 未知 + //}; + //Q_DECLARE_FLAGS(CursorTypes, ScreenStatus) // 枚举 ScrnType 生成宏 CursorTypes + // Q_DECLARE_OPERATORS_FOR_FLAGS(CursorTypes) // 重载宏 ScrnType 的 |() 函数 + + // C++11 新增带作用域的枚举,用 enum class 或enum struct(两者等价)声明。 + // https://blog.csdn.net/luckysym/article/details/1666114 + enum class DrawState { + Draw, + Move, + Delete, + Wateing, + Unknow + }; + + enum class DrawShape { + NoDraw, + Rectangles, + Ellipses, + LineWidth, // 特殊、线宽度 + Arrows, + Pen, + Mosaics, + Text, + SerialNumber + }; + + enum class ToolBarOffset { + TBO_Left, + TBO_Middle, + TBO_Right, + + TBO_Top = TBO_Left, + TBO_Bottom = TBO_Right + }; + + enum class LineEnds { + EmptyToEmpty, + EmptyToRecesedArrow, + DotToRecesedArrow, + EmptyToArrow, + DotToArrow, + EmptyToDot, + DotToEmpty, + DotToDot, + RecesedArrowToRecesedArrow, + ArrowToArrow + }; +} + +using namespace XC; +// NoDraw, +// Rectangles, +// Ellipses, +// Line, +// Arrows, +// Pen, +// Mosaics, +// Text, +// SerialNumber + +struct XDrawStep +{ + //new refactor + // base element ------------------ + QPoint p1; // 起点 + QPoint p2; // 终点 + QRect rt; // 初始绘画位置: 由 p1、p2 构成 + DrawShape shape = DrawShape::NoDraw; // 绘画形状 + int bStyele = 0; // refactor 图形的样式, 取代之前 bFill + int tbIdx = 0; // 某个绘画类型的样式: 为 ParameterBar 的子序号 + int idxLevel = -1; // 所处的序号等级,亦 z 轴,越大越顶层; 此项待重构 + static int totalIdx; // 所有已绘画类型 的总的序号,越大越上层 + + // color, pen width + QPen pen = QPen(Qt::red, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); // 默认红色,画笔宽度为 2px + QBrush brush = QBrush(Qt::NoBrush); // 画刷 + + // Rectangles -------------------- + // Ellipses ---------------------- + // Line / Arrows ----------------- + LineEnds lineEnds = LineEnds::EmptyToEmpty; + + // Arrows ------------------------ + // Pen --------------------------- + QVector custPath; // 手绘路径 + + // Mosaics ----------------------- + int mscPx = 3; // 马赛克默认模糊块 + + // Text -------------------------- + bool bDisplay = false; // true 准备显示,显示;false 显示结束,隐藏起来 + bool bTextComplete = true; // 文字编辑完成 + QPoint editPos; // 显示控件 + QString text = "[Test Text]"; // 文字内容 + QFont font; // 字体 + + // SerialNumber ------------------ + + void clear() { + p1 = QPoint(); + p2 = QPoint(); + rt = QRect(); + //shape = DrawShape::NoDraw; // 若为鼠标松开执行,则会无法继续绘画抽象图形 + //pen = QPen(Qt::red); + //penWidth = 2; + //brush = QBrush(Qt::NoBrush); + //brushWidth = 1; + //transparency = 1; + //rX = 8; + //rY = 8; + //lineEnd = LineEnds::EmptyToEmpty; + //lineDashe = LineDashes::SolidLine; + custPath.clear(); + //text = "==Test Text=="; + //font = QFont(); + //fontSize = 16; + //mscPx = 3; + + idxLevel = -1; + //g_index = -1; 永不重置 + + } +}; + +// ------------------------ +class XHelper : public QObject +{ + Q_OBJECT +public: + static XHelper& instance() { + static XHelper instance; + return instance; + } + + XHelper(XHelper&&) = delete; + XHelper(const XHelper&) = delete; + void operator= (const XHelper&) = delete; +private: + XHelper(); + virtual ~XHelper() = default; + +public: + double getScale(QScreen* screen = QApplication::primaryScreen()); + + void setBoardStyle(const int index) { m_boardStyleIndex = index; } + const int boardStyle() { return m_boardStyleIndex; } + void setBorderColor(QColor color) { m_borderColor = color; } + const QColor borderColor() { return m_borderColor; } + void setBorderWidth(const int width) { m_borderWidth = width; } + const int borderWidth() { return m_borderWidth; } + void setCrosshairColor(QColor color) { m_crosshairColor = color; } + const QColor crosshairColor() { return m_crosshairColor; } + void setCrosshairWidth(const int width) { m_crosshairWidth = width; } + const int crosshairWidth() { return m_crosshairWidth; } + bool smartWindow() const { return m_enableSmartWindow; } + void setSmartWindow(bool enable) { m_enableSmartWindow = enable; } + bool crosshair() const { return m_enableCrosshair; } + void setCrosshair(bool enable) { m_enableCrosshair = enable; } + bool showCursor() const { return m_enableShowCursor; } + void setShowCursor(bool enable) { m_enableShowCursor = enable; } + bool autoCpoyClip() const { return m_enableAutoCpoyClip; } + void setAutoCpoyClip(bool enable) { m_enableAutoCpoyClip = enable; } + + int imgQuailty() const { return m_imgQuailty; } + void setImgQuailty(int val) { m_imgQuailty = val; } + const QString formatToName(const QString str = XHelper::instance().path(toFileName)); + + QIcon ChangeSVGColor(QString path, QString shape, QColor color, QSize size); + void SetAttrRecur(QDomElement& elem, QString strtagname, QString strattr, QString strattrval); + + // Mosaics draw + const QPixmap* SetMosaicSmooth(QPixmap* pixmap, int px); // 毛玻璃马赛克 + const QImage SetMosaicPixlelated(QPixmap* pixmap, int px = 20); // 像素级马赛克 + + // Arrow Line draw + QPainterPath GetArrowHead(QPoint p1, QPoint p2, const int thickness = 10); + QLine GetShorterLine(QPoint p1, QPoint p2, const int thickness = 10); + + // tabGeneral + // tabInterface + + // tabOutput + QString path(const QString key) const; + void setPath(const QString key, const QString val); + + // tabPin + bool winShadow() const; + void setWinShadow(bool enable); + + int pinOpacity() const; + void setPinOpacity(int opacity); + int pinMaxSize() const; + void setPinMaxSize(int val); + +signals: + void sigChangeWinShadow(bool enable); + void sigChangeOpacity(int opacity); + void sigChangeMaxSize(int val); + +private: + int m_boardStyleIndex; + QColor m_borderColor; // 边框 + int m_borderWidth; + QColor m_crosshairColor; // 边框 + int m_crosshairWidth; + bool m_enableSmartWindow; + bool m_enableCrosshair; + bool m_enableShowCursor; + bool m_enableAutoCpoyClip; + + int m_imgQuailty; + QMap m_path; + + bool m_winShadow; + int m_pinOpacity; + int m_pinMaxSize; +}; + +// ------------------------ +// 创建全局静态 单例 的对象, 就不浪费生命重新创建了, 路径后面替换为 ConfigLocation +Q_GLOBAL_STATIC_WITH_ARGS(QSettings, insSettings, (qApp->applicationDirPath() + "/config.ini", QSettings::IniFormat)); +//Q_GLOBAL_STATIC(XHelper, insXHelp); + + // perference UI config +const QString INIT_GENERAL("General"); // 初始化 常规 +const QString INIT_INTERFACE("Interface"); // 初始化 界面 +const QString INIT_OUTPUT("Output"); // 初始化 输出 +const QString INIT_PIN("Pin"); // 初始化 贴图 +const QString INIT_HOTKEYS("Hotkeys"); // 初始化 快捷键 + +#endif // XDRAW_H diff --git a/src/screen/rectcalcu.cpp b/src/screen/rectcalcu.cpp index dfc02e0..442d4cf 100644 --- a/src/screen/rectcalcu.cpp +++ b/src/screen/rectcalcu.cpp @@ -1,270 +1,244 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2021.09.29 - * Description: - ******************************************************************/ -#include "rectcalcu.h" -#include -#include -#include "screenshot.h" -#include "../core/xlog.h" - -//// 通过任意两点获取一个选中矩形 -//const QRect& RectCalcu::setSelRect() -//{ -// if (scrnType == ScrnType::Move) { -// rtSel.translate(pos2 - pos1); -// } else if (scrnType == ScrnType::Stretch) { -// if (!rtSel.isValid()) -// rtSel = QRect(); -// else -// rtSel = getStretchRect(); -// } -// -// return rtSel; -//} - -// 返回副本 -const QRect RectCalcu::getStretchRect() -{ - QRect rt; - if (scrnType != ScrnType::Stretch) - return rt; - - rt = rtSel; - const int width = pos2.x() - pos1.x(); - const int height = pos2.y() - pos1.y(); - const QPoint offset(pos2 - pos1); - const CursorArea cursArea = getCursorArea(pos1, true); - - //XLOG_DEBUG("a1 拉伸返回副本 => cursArea[{}], offset({}, {}) rt({}, {}, {} * {})] pos1({}, {}) pos2({}, {})" - // , (int)cursArea, offset.x(), offset.y(), rt.x(), rt.y(), rt.width(), rt.height(), pos1.x(), pos1.y(), pos2.x(), pos2.y()); - - if (cursArea == CursorArea::Left) { - rt.setLeft(rt.left() + width); - } else if (cursArea == CursorArea::Top) { - rt.setTop(rt.top() + height); - } else if (cursArea == CursorArea::Right) { - rt.setRight(rt.right() + width); - } else if (cursArea == CursorArea::Bottom) { - rt.setBottom(rt.bottom() + height); - } else if (cursArea == CursorArea::TopLeft) { - rt.setTopLeft(rt.topLeft() + offset); - } else if (cursArea == CursorArea::TopRight) { - rt.setTopRight(rt.topRight() + offset); - } else if (cursArea == CursorArea::BottomLeft) { - rt.setBottomLeft(rt.bottomLeft() + offset); - } else if (cursArea == CursorArea::BottomRight) { - rt.setBottomRight(rt.bottomRight() + offset); - } else { - XLOG_ERROR("此时处于未知状态 CursorArea::Unknow 513"); - } - - //XLOG_DEBUG("a2 拉伸返回副本 => cursArea[{}], offset({}, {}) rt({}, {}, {} * {})] pos1({}, {}) pos2({}, {})" - // , (int)cursArea, offset.x(), offset.y(), rt.x(), rt.y(), rt.width(), rt.height(), pos1.x(), pos1.y(), pos2.x(), pos2.y()); - // 修复拉伸矩形为负数但却可以绘画的场景 - if (!rt.isValid()) { - if (rt.width() <= 0 || rt.height() <= 0) { - QPoint p1(rt.topLeft()); - QPoint p2(rt.topLeft() + QPoint(rt.width(), rt.height())); - rt = getRect(p1, p2); - } - } - - //XLOG_DEBUG("a3 拉伸返回副本 => cursArea[{}], offset({}, {}) rt({}, {}, {} * {})] pos1({}, {}) pos2({}, {})" - // , (int)cursArea, offset.x(), offset.y(), rt.x(), rt.y(), rt.width(), rt.height(), pos1.x(), pos1.y(), pos2.x(), pos2.y()); - return rt; -} - -// 计算后得出结果数据,其余归零 -const QRect RectCalcu::calcurRsultOnce() -{ - rtSel = getSelRect(); - - pos1 = QPoint(); - pos2 = QPoint(); - - return rtSel; -} - -// 仅被智能窗口选中时候使用,不要随意修改此数值; -void RectCalcu::setRtSel(const QRect rt) -{ - rtSel = rt; - - pos1 = rtSel.topLeft(); - pos2 = rtSel.bottomRight(); -} - -// 限制选中矩形不超过虚拟屏幕的边界, rect 为当前选中矩形 -QRect& RectCalcu::limitBound(QRect& rt, QRect rtDesktop) -{ - if (!rt.isValid()) - return rt; - - if (rt.left() <= rtDesktop.left()) - rt.setLeft(rtDesktop.left()); - if (rt.top() <= rtDesktop.top()) - rt.setTop(rtDesktop.top()); - if (rt.right() >= rtDesktop.right()) - rt.setRight(rtDesktop.right()); - if (rt.bottom() >= rtDesktop.bottom()) - rt.setBottom(rtDesktop.bottom()); - - return rt; -} - - -// 判断当前鼠标所在区域; false 为大概区域(用来粗略计算显示光标区域即可); true 为详细区域(精确计算所在区域,需要修改矩形大小做准备,显示不同光标) -const CursorArea RectCalcu::getCursorArea(const QPoint pos, bool details) -{ - QRect rt(rtSel); - if (!rt.isValid() || !rt.isValid()) - return CursorArea::External; - - QRect rtOuter = getExteRect(rt); - QRect rtInner = getInteRect(rt); - int interval = (rtOuter.height() - rtInner.height()) / 2; - - - // XLOG_DEBUG("矩形测试 rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {})" - // , rtOuter.x(), rtOuter.y(), rtOuter.width(), rt.height() - //, rtOuter.left(), rtOuter.top(), rtOuter.width(), rt.height() - //, rtOuter.topLeft().x(), rtOuter.topLeft().y(), rtOuter.width(), rt.height() - //, rtOuter.x(), rtOuter.y(), rtOuter.width(), rt.height() - // , rtOuter.left(), rtOuter.left(), rtOuter.width(), rt.height() - // , interval); - - //XLOG_DEBUG("区域分割 rtOuter({}, {}, {} * {}) rtInner({}, {}, {} * {}) interval{}" - // , rtOuter.left() , rtOuter.left(), rtOuter.width(), rtOuter.height() - // , rtInner.left(), rtInner.left(), rtInner.width(), rtInner.height() - // , interval); - - QRect rtLeft(rtOuter.left(), rtInner.top(), interval, rtInner.height()); - QRect rtTop(rtInner.left(), rtOuter.top(), rtInner.width(), interval); - QRect rtRight(rtInner.right(), rtInner.top(), interval, rtInner.height()); - QRect rtBottom(rtInner.left(), rtInner.bottom(), rtInner.width(), interval); - QRect rtTopLeft(rtOuter.left(), rtOuter.top(), interval, interval); - QRect rtTopRight(rtInner.right(), rtOuter.top(), interval, interval); - QRect rtBottomLeft(rtOuter.left(), rtInner.bottom(), interval, interval); - QRect rtBottomRight(rtInner.right(), rtInner.bottom(), interval, interval); - - // 内部、外部、边框 - if (rtInner.contains(pos, true)) { - return CursorArea::Internal; - } else if (!rtOuter.contains(pos, true)) { - return CursorArea::External; - //} else if (!rtInner.contains(pos, true) && rtOuter.contains(pos, false)) { - } else { - if (!details) {// 模糊区域 - return CursorArea::Border; - } else { // 需要更详细区域 - if (rtLeft.contains(pos, true)) - return CursorArea::Left; - else if (rtRight.contains(pos, true)) - return CursorArea::Right; - else if (rtTop.contains(pos, true)) - return CursorArea::Top; - else if (rtBottom.contains(pos, true)) - return CursorArea::Bottom; - else if (rtTopLeft.contains(pos, true)) - return CursorArea::TopLeft; - else if (rtBottomRight.contains(pos, true)) - return CursorArea::BottomRight; - else if (rtTopRight.contains(pos, true)) - return CursorArea::TopRight; - else if (rtBottomLeft.contains(pos, true)) - return CursorArea::BottomLeft; - else - return CursorArea::Unknow; - } - } -} - -QRect RectCalcu::getRect(QPoint pos1, QPoint pos2) -{ - int xMin = qMin(pos1.x(), pos2.x()); - int xMax = qMax(pos1.x(), pos2.x()); - int yMin = qMin(pos1.y(), pos2.y()); - int yMax = qMax(pos1.y(), pos2.y()); - - if (xMin == xMax && yMin == yMax) // 若是重复点,则会返回 QRect(0, 0, 1, 1); - return QRect(0, 0, -1, -1); - else - return QRect(QPoint(xMin, yMin), QPoint(xMax, yMax)); - -} - -RectCalcu::RectCalcu(ScreenShot* pSrnShot) - : pos1(0, 0) - , pos2(0, 0) - , scrnType(ScrnType::Wait) - , rtSel(0, 0, -1, -1) - , m_bClear(false) - , cursArea(CursorArea::Unknow) - , m_pSrnShot(pSrnShot) -{ -} - -RectCalcu::~RectCalcu() -{ -} - -// 此时并不修改其 resel 数值 -const QRect RectCalcu::getSelRect() -{ - if (scrnType == ScrnType::Select) { - return getRect(pos1, pos2); - } else if (scrnType == ScrnType::Move) { - // TODO 2022.06.14: 单例改写了为 new 形式,故此处有一个 移动图形的 bug,且略感觉此获取方式有点不优雅 - if (m_pSrnShot && m_pSrnShot->isSelBorder()) // 选中绘画的矩形 - return rtSel.translated(pos2 - pos1); - - return rtSel; - } else if (scrnType == ScrnType::Stretch) { - return getStretchRect(); // 返回副本 - - } else { - return rtSel; - } -} - -QRect RectCalcu::getExteRect(QRect& rect, int interval) -{ - QPoint topLeft = rect.topLeft(); - QPoint bottomRight = rect.bottomRight();// +QPoint(1, 1); - return QRect(QPoint(topLeft.x() - interval, topLeft.y() - interval), QPoint(bottomRight.x() + interval, bottomRight.y() + interval)); -} - -QRect RectCalcu::getInteRect(QRect& rect, int interval) -{ - // TODO 2021-10-01: 考虑 rect 的width 和 height 本身 <= 2 * HAIF_INTERVAL? - QPoint topLeft = rect.topLeft(); - QPoint bottomRight = rect.bottomRight();// +QPoint(1, 1); - return QRect(QPoint(topLeft.x() + interval, topLeft.y() + interval), QPoint(bottomRight.x() - interval, bottomRight.y() - interval)); -} - - -void RectCalcu::clear() -{ - pos1 = QPoint(); - pos2 = QPoint(); - rtSel = QRect(); - scrnType = ScrnType::Wait; - m_bClear = true; -} - -void RectCalcu::setClear(bool clear) -{ - m_bClear = clear; -} - -bool RectCalcu::isClear() -{ - return m_bClear; -} +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2021.09.29 + * Description: + ******************************************************************/ +#include "rectcalcu.h" +#include +#include +#include "screenshot.h" +#include "../core/xlog.h" + +// 返回副本 +const QRect RectCalcu::getStretchRect() +{ + QRect rt; + if (scrnType != ScrnType::Stretch) + return rt; + + rt = rtSel; + const int width = pos2.x() - pos1.x(); + const int height = pos2.y() - pos1.y(); + const QPoint offset(pos2 - pos1); + const CursorArea cursArea = getCursorArea(pos1, true); + + if (cursArea == CursorArea::Left) { + rt.setLeft(rt.left() + width); + } else if (cursArea == CursorArea::Top) { + rt.setTop(rt.top() + height); + } else if (cursArea == CursorArea::Right) { + rt.setRight(rt.right() + width); + } else if (cursArea == CursorArea::Bottom) { + rt.setBottom(rt.bottom() + height); + } else if (cursArea == CursorArea::TopLeft) { + rt.setTopLeft(rt.topLeft() + offset); + } else if (cursArea == CursorArea::TopRight) { + rt.setTopRight(rt.topRight() + offset); + } else if (cursArea == CursorArea::BottomLeft) { + rt.setBottomLeft(rt.bottomLeft() + offset); + } else if (cursArea == CursorArea::BottomRight) { + rt.setBottomRight(rt.bottomRight() + offset); + } else { + XLOG_ERROR("此时处于未知状态 CursorArea::Unknow 513"); + } + + // 修复拉伸矩形为负数但却可以绘画的场景 + if (!rt.isValid()) { + if (rt.width() <= 0 || rt.height() <= 0) { + QPoint p1(rt.topLeft()); + QPoint p2(rt.topLeft() + QPoint(rt.width(), rt.height())); + rt = getRect(p1, p2); + } + } + + return rt; +} + +// 计算后得出结果数据,其余归零 +const QRect RectCalcu::calcurRsultOnce() +{ + rtSel = getSelRect(); + pos1 = QPoint(); + pos2 = QPoint(); + + return rtSel; +} + +// 仅被智能窗口选中时候使用,不要随意修改此数值; +void RectCalcu::setRtSel(const QRect rt) +{ + rtSel = rt; + pos1 = rtSel.topLeft(); + pos2 = rtSel.bottomRight(); +} + +// 限制选中矩形不超过虚拟屏幕的边界, rect 为当前选中矩形 +QRect& RectCalcu::limitBound(QRect& rt, QRect rtDesktop) +{ + if (!rt.isValid()) + return rt; + if (rt.left() <= rtDesktop.left()) + rt.setLeft(rtDesktop.left()); + if (rt.top() <= rtDesktop.top()) + rt.setTop(rtDesktop.top()); + if (rt.right() >= rtDesktop.right()) + rt.setRight(rtDesktop.right()); + if (rt.bottom() >= rtDesktop.bottom()) + rt.setBottom(rtDesktop.bottom()); + + return rt; +} + + +// 判断当前鼠标所在区域; false 为大概区域(用来粗略计算显示光标区域即可); true 为详细区域(精确计算所在区域,需要修改矩形大小做准备,显示不同光标) +const CursorArea RectCalcu::getCursorArea(const QPoint pos, bool details) +{ + QRect rt(rtSel); + if (!rt.isValid() || !rt.isValid()) + return CursorArea::External; + + QRect rtOuter = getExteRect(rt); + QRect rtInner = getInteRect(rt); + int interval = (rtOuter.height() - rtInner.height()) / 2; + + // XLOG_DEBUG("矩形测试 rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {}) rtOuter0({}, {}, {} * {})" + // , rtOuter.x(), rtOuter.y(), rtOuter.width(), rt.height() + //, rtOuter.left(), rtOuter.top(), rtOuter.width(), rt.height() + //, rtOuter.topLeft().x(), rtOuter.topLeft().y(), rtOuter.width(), rt.height() + //, rtOuter.x(), rtOuter.y(), rtOuter.width(), rt.height() + // , rtOuter.left(), rtOuter.left(), rtOuter.width(), rt.height() + // , interval); + + //XLOG_DEBUG("区域分割 rtOuter({}, {}, {} * {}) rtInner({}, {}, {} * {}) interval{}" + // , rtOuter.left() , rtOuter.left(), rtOuter.width(), rtOuter.height() + // , rtInner.left(), rtInner.left(), rtInner.width(), rtInner.height() + // , interval); + + QRect rtLeft(rtOuter.left(), rtInner.top(), interval, rtInner.height()); + QRect rtTop(rtInner.left(), rtOuter.top(), rtInner.width(), interval); + QRect rtRight(rtInner.right(), rtInner.top(), interval, rtInner.height()); + QRect rtBottom(rtInner.left(), rtInner.bottom(), rtInner.width(), interval); + QRect rtTopLeft(rtOuter.left(), rtOuter.top(), interval, interval); + QRect rtTopRight(rtInner.right(), rtOuter.top(), interval, interval); + QRect rtBottomLeft(rtOuter.left(), rtInner.bottom(), interval, interval); + QRect rtBottomRight(rtInner.right(), rtInner.bottom(), interval, interval); + + // 内部、外部、边框 + if (rtInner.contains(pos, true)) { + return CursorArea::Internal; + } else if (!rtOuter.contains(pos, true)) { + return CursorArea::External; + //} else if (!rtInner.contains(pos, true) && rtOuter.contains(pos, false)) { + } else { + if (!details) {// 模糊区域 + return CursorArea::Border; + } else { // 需要更详细区域 + if (rtLeft.contains(pos, true)) + return CursorArea::Left; + else if (rtRight.contains(pos, true)) + return CursorArea::Right; + else if (rtTop.contains(pos, true)) + return CursorArea::Top; + else if (rtBottom.contains(pos, true)) + return CursorArea::Bottom; + else if (rtTopLeft.contains(pos, true)) + return CursorArea::TopLeft; + else if (rtBottomRight.contains(pos, true)) + return CursorArea::BottomRight; + else if (rtTopRight.contains(pos, true)) + return CursorArea::TopRight; + else if (rtBottomLeft.contains(pos, true)) + return CursorArea::BottomLeft; + else + return CursorArea::Unknow; + } + } +} + +QRect RectCalcu::getRect(QPoint pos1, QPoint pos2) +{ + int xMin = qMin(pos1.x(), pos2.x()); + int xMax = qMax(pos1.x(), pos2.x()); + int yMin = qMin(pos1.y(), pos2.y()); + int yMax = qMax(pos1.y(), pos2.y()); + + if (xMin == xMax && yMin == yMax) // 若是重复点,则会返回 QRect(0, 0, 1, 1); + return QRect(0, 0, -1, -1); + else + return QRect(QPoint(xMin, yMin), QPoint(xMax, yMax)); + +} + +RectCalcu::RectCalcu(ScreenShot* pSrnShot) + : pos1(0, 0) + , pos2(0, 0) + , scrnType(ScrnType::Wait) + , rtSel(0, 0, -1, -1) + , m_bClear(false) + , cursArea(CursorArea::Unknow) + , m_pSrnShot(pSrnShot) +{ +} + +RectCalcu::~RectCalcu() +{ +} + +// 此时并不修改其 resel 数值 +const QRect RectCalcu::getSelRect() +{ + if (scrnType == ScrnType::Select) { + return getRect(pos1, pos2); + } else if (scrnType == ScrnType::Move) { + // TODO 2022.06.14: 单例改写了为 new 形式,故此处有一个 移动图形的 bug,且略感觉此获取方式有点不优雅 + if (m_pSrnShot && m_pSrnShot->isSelBorder()) // 选中绘画的矩形 + return rtSel.translated(pos2 - pos1); + + return rtSel; + } else if (scrnType == ScrnType::Stretch) { + return getStretchRect(); // 返回副本 + + } else { + return rtSel; + } +} + +QRect RectCalcu::getExteRect(QRect& rect, int interval) +{ + QPoint topLeft = rect.topLeft(); + QPoint bottomRight = rect.bottomRight();// +QPoint(1, 1); + return QRect(QPoint(topLeft.x() - interval, topLeft.y() - interval), QPoint(bottomRight.x() + interval, bottomRight.y() + interval)); +} + +QRect RectCalcu::getInteRect(QRect& rect, int interval) +{ + // TODO 2021-10-01: 考虑 rect 的width 和 height 本身 <= 2 * HAIF_INTERVAL? + QPoint topLeft = rect.topLeft(); + QPoint bottomRight = rect.bottomRight();// +QPoint(1, 1); + return QRect(QPoint(topLeft.x() + interval, topLeft.y() + interval), QPoint(bottomRight.x() - interval, bottomRight.y() - interval)); +} + + +void RectCalcu::clear() +{ + pos1 = QPoint(); + pos2 = QPoint(); + rtSel = QRect(); + scrnType = ScrnType::Wait; + m_bClear = true; +} + +void RectCalcu::setClear(bool clear) +{ + m_bClear = clear; +} + +bool RectCalcu::isClear() +{ + return m_bClear; +} diff --git a/src/screen/screenshot.cpp b/src/screen/screenshot.cpp index 8cc5a0d..6d70020 100644 --- a/src/screen/screenshot.cpp +++ b/src/screen/screenshot.cpp @@ -1,1631 +1,1614 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2021.09.29 - * Description: - ******************************************************************/ -#include "screenshot.h" -#include "../core/xlog.h" -#include "../platform/wininfo.h" -#include "../tool/pin/pinwidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "QDir" - - -namespace Util { - bool getRectFromCurrentPoint(WinID winId, QRect &outRect) - { - const QRect rt = WinInfo::instance().targWinRect(winId, false); - - outRect.setLeft(rt.left()); - outRect.setTop(rt.top()); - outRect.setRight(rt.right()); - outRect.setBottom(rt.bottom()); - return true; - } -} - -ScreenShot::ScreenShot(QWidget *parent) - : QWidget(parent) - , m_scal(XHelper::instance().getScale()) - , m_scrns(qGuiApp->screens()) - , m_priScrn(qGuiApp->primaryScreen()) - , m_virGeom(m_priScrn->virtualGeometry()) - , m_currPixmap(nullptr) - , m_rtCalcu(this) - , m_bSmartWin(XHelper::instance().smartWindow()) - , m_bFirstSel(false) - , m_bFirstPress(false) - , m_pCurrShape(nullptr) - , m_textEdit(new XTextWidget(this)) - , m_rtSmartWindow(0, 0, 0, 0) - , m_barOrien(Qt::Horizontal) // Horizontal | Vertical - , m_selSize(new SelectSize("test", this)) - , m_selBar(new SelectBar(m_barOrien, this)) - , m_paraBar(new ParameterBar(m_barOrien, this)) -{ - XLOG_INFO("bootUniqueId[{}]", QSysInfo::bootUniqueId().data()); - XLOG_INFO("buildAbi[{}]", QSysInfo::buildAbi().toUtf8().data()); - XLOG_INFO("buildCpuArchitecture[{}]", QSysInfo::buildCpuArchitecture().toUtf8().data()); - XLOG_INFO("currentCpuArchitecture[{}]", QSysInfo::currentCpuArchitecture().toUtf8().data()); - XLOG_INFO("kernelType[{}]", QSysInfo::kernelType().toUtf8().data()); - XLOG_INFO("kernelVersion[{}]", QSysInfo::kernelVersion().toUtf8().data()); - XLOG_INFO("machineHostName[{}]", QSysInfo::machineHostName().toUtf8().data()); - XLOG_INFO("machineUniqueId[{}]", QSysInfo::machineUniqueId().data()); - XLOG_INFO("prettyProductName[{}]", QSysInfo::prettyProductName().toUtf8().data()); - XLOG_INFO("productType[{}]", QSysInfo::productType().toUtf8().data()); - XLOG_INFO("productVersion[{}]", QSysInfo::productVersion().toUtf8().data()); - - setAttribute(Qt::WA_DeleteOnClose, true); - //setAttribute(Qt::WA_QuitOnClose, false); - - //QFont font("STXingkai", 40); // 设置默认值 - //m_textEdit->setFont(font); - m_textEdit->setTextColor(Qt::red); - m_textEdit->hide(); - - // 注意显示器摆放的位置不相同~;最大化的可能异常修复 -#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) - setWindowFlags(Qt::FramelessWindowHint | windowFlags()); // | Qt::WindowStaysOnTopHint - #ifdef _MYDEBUG - setWindowFlag(Qt::WindowStaysOnTopHint, false); // 删除置顶 - m_virGeom = currentScreen(QCursor::pos())->geometry(); - if (m_scrns.size() == 1) - m_virGeom.setWidth(m_virGeom.width() / 2); - #endif - - setFixedSize(m_virGeom.size()); -#else // Q_OS_MAC -// setWindowFlags(Qt::Window); // 不设置则 mac 下 devicePixelRatio: 1 - #ifdef _MYDEBUG - m_virGeom = currentScreen(QCursor::pos())->geometry(); - if (m_scrns.size() == 1) - m_virGeom.setWidth(m_virGeom.width() / 2); - setFixedSize(m_virGeom.size()); - #else - showFullScreen(); - #endif -#endif - - move(m_virGeom.topLeft()); - setMouseTracking(true); - m_rtCalcu.scrnType = ScrnType::Wait; - - // new refactor - m_selSize->setVisible(false); - m_selBar->setVisible(false); - m_paraBar->setVisible(false); - connect(m_selBar, &SelectBar::sigEnableDraw, this, &ScreenShot::onEnableDraw); - connect(m_selBar, &SelectBar::sigSelShape, this, &ScreenShot::onSelShape); - connect(m_selBar, &SelectBar::sigRevocation, this, &ScreenShot::onRevocation); - connect(m_selBar, &SelectBar::sigRenewal, this, &ScreenShot::onRenewal); - connect(m_selBar, &SelectBar::sigPin, this, &ScreenShot::onPin); - connect(m_selBar, &SelectBar::sigSave, this, &ScreenShot::onSave); - connect(m_selBar, &SelectBar::sigCancel, this, &ScreenShot::onCancel); - connect(m_selBar, &SelectBar::sigFinish, this, &ScreenShot::onFinish); - - connect(m_paraBar, &ParameterBar::sigParaBtnId, this, &ScreenShot::onParaBtnId); - connect(m_paraBar, &ParameterBar::sigSelColor, this, &ScreenShot::onSelColor); - - connect(m_selBar, &SelectBar::sigEnableDraw, m_paraBar, &ParameterBar::onEnableDraw); - connect(m_selBar, &SelectBar::sigSelShape, m_paraBar, &ParameterBar::onSelShape); - connect(this, &ScreenShot::sigClearScreen, this, &ScreenShot::onClearScreen); -} - -ScreenShot::~ScreenShot() -{ -} - -ScrnType ScreenShot::updateScrnType(const QPoint pos) -{ - CursorArea cursArea = m_rtCalcu.getCursorArea(pos); - - // 后面加上绘画和选择 - if (cursArea == CursorArea::Internal) { - return ScrnType::Move; - } else if (cursArea == CursorArea::External) { - return ScrnType::Wait; - } else if (cursArea == CursorArea::Border) { - return ScrnType::Stretch; - } else { - return ScrnType::Wait; // 避免警告,不会运行到此 - } -} - -void ScreenShot::updateCursorShape(const QPoint pos) -{ - if (m_rtCalcu.scrnType == ScrnType::Draw) - return; - - CursorArea cursArea = m_rtCalcu.getCursorArea(pos, true); - if (m_rtCalcu.scrnType == ScrnType::Move) { - } else if (m_rtCalcu.scrnType == ScrnType::Select) { - setCursor(Qt::CrossCursor); - } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { - } else if (m_rtCalcu.scrnType == ScrnType::Wait) { - if (!XHelper::instance().smartWindow()) - return; - - if (cursArea == CursorArea::External) { - - (m_rtSmartWindow.contains(pos, false) && !m_bFirstSel) || m_rtCalcu.getSelRect().contains(pos, true) ? - setCursor(Qt::ArrowCursor) : setCursor(Qt::ForbiddenCursor); - } else if (cursArea == CursorArea::Internal){ - setCursor(Qt::SizeAllCursor); - // 已修复:光标移动到绘画工具栏时,光标显示异常,在里面的事件过滤器中修复此出逻辑纰漏。光标进入 tbBar 后, - // 光标坐标也【不会开始改变】,至少在 ScreenShot。 - } else { - updateBorderCursorShape(cursArea); - } - } -} - -// 功能函数,仅考虑边框状态 -void ScreenShot::updateBorderCursorShape(const CursorArea & cursArea) -{ - if (cursArea == CursorArea::Top || cursArea == CursorArea::Bottom) - setCursor(Qt::SizeVerCursor); - else if (cursArea == CursorArea::Left || cursArea == CursorArea::Right) - setCursor(Qt::SizeHorCursor); - else if (cursArea == CursorArea::TopLeft || cursArea == CursorArea::BottomRight) - setCursor(Qt::SizeFDiagCursor); - else if (cursArea == CursorArea::TopRight || cursArea == CursorArea::BottomLeft) - setCursor(Qt::SizeBDiagCursor); -} - -// 清空截图内容(当关闭 Esc、或完成截图时) -void ScreenShot::onClearScreen() -{ -#ifdef Q_OS_WIN - - -#elif defined(Q_OS_MAC) - setWindowFlags(Qt::SubWindow); - showNormal(); -#elif defined(Q_OS_LINUX) -#endif - - m_bFirstPress = false; - m_vWholeScrn.clear(); - - //m_screens、m_primaryScreen 还保留 - delete m_currPixmap; - m_currPixmap = nullptr; - - m_pCurrShape = nullptr; - - m_vDrawed.clear(); - m_vDrawUndo.clear(); - - m_rtCalcu.clear(); - - m_bFirstSel = false; - m_selSize->setVisible(false); - m_selBar->setVisible(false); - m_paraBar->setVisible(false); - m_step.clear(); - - m_rtSmartWindow = QRect(); - if (m_rtCalcu.scrnType == ScrnType::Wait) // 状态重置 - setMouseTracking(true); -}; - -void ScreenShot::onLineEndsChange(LineEnds ends) -{ - m_step.lineEnds = ends; -} - -void ScreenShot::onLineDasheChange(Qt::PenStyle dashes) -{ - m_step.pen.setStyle(dashes); -} - -void ScreenShot::onEnableDraw(bool enable) -{ - if (enable) { - m_rtCalcu.scrnType = ScrnType::Draw; - setMouseTracking(false); // Fix: 鼠标移动中会被自动绘画矩形,副作用绘画状态的光标不完美了(选中框内外的光标被固定了),严格不算 bug,一种外观特效 - // qInfo()<<"--------------onDrawStart"<(); - } else if (bLine) { - //int idx = property(std::to_string((int)shape).c_str()).value(); - //int penWidth = 0; - - //if (idx == 0) - // penWidth = 1; - //else if (idx == 1) - // penWidth = 2; - //else if (idx == 2) - // penWidth = 4; - } else if (bPen) { - } else if (bArrows) { - m_step.bStyele = property(std::to_string((int)shape).c_str()).value(); - } else if (bText) { - } else if (bSeriNum) { - } else { - //XLOG_INFO - } - - - - //qDebug() << "--------@onDrawShape:" << int(m_step.shape); -} - -void ScreenShot::onRevocation() -{ - if (m_vDrawed.count() <= 0) - return; - - m_vDrawUndo.push_back(*(m_vDrawed.end() - 1)); - m_vDrawed.pop_back(); - qDebug() << "---->m_vDrawRevoke:" << m_vDrawUndo.count() << " m_vDraw:" << m_vDrawed.count(); - update(); -} - -void ScreenShot::onRenewal() -{ - if (m_vDrawUndo.count() <= 0) - return; - - m_vDrawed.push_back(*(m_vDrawUndo.end() - 1)); - m_vDrawUndo.pop_back(); - qDebug() << "---->m_vDrawRevoke:" << m_vDrawUndo.count() << " m_vDraw:" << m_vDrawed.count(); - update(); -} - -void ScreenShot::onPin() -{ - if (m_savePixmap.isNull()) - return; - - auto pin = new PinWidget(m_savePixmap, m_savePixmap.rect(), nullptr); // 使用 nullptr,不会泄露 - pin->move(m_rtCalcu.getSelRect().topLeft()); - pin->show(); - - clearnAndClose(); -} - -void ScreenShot::onSave() -{ - if (drawToCurrPixmap()) { - if (XHelper::instance().autoCpoyClip()) - QApplication::clipboard()->setPixmap(m_savePixmap); - - const QString imageName = XHelper::instance().formatToName(); - QString fileter(tr("Image Files(*.png);;Image Files(*.jpg);;All Files(*.*)")); - QString fileNmae = QFileDialog::getSaveFileName(this, tr("Save Files"), imageName, fileter); - if (fileNmae.isEmpty()) - return; - - QTime startTime = QTime::currentTime(); - m_savePixmap.save(fileNmae, nullptr, XHelper::instance().imgQuailty()); // 绘画在 m_savePixmap 中,若在 m_savePixmap 会有 selRect 的左上角的点的偏移 - QTime stopTime = QTime::currentTime(); - int elapsed = startTime.msecsTo(stopTime); - qDebug() << "save m_savePixmap tim =" << elapsed << "ms" << m_savePixmap.size(); - //m_currPixmap->save("a2.png"); - - // auto save pixmap - const QString path = XHelper::instance().formatToName(XHelper::instance().path(toAutoSavePath).trimmed()); - if (!path.isEmpty()) { - m_savePixmap.save(path + QDir::separator() + imageName); - } - } - - clearnAndClose(); -} - -void ScreenShot::clearnAndClose() -{ - emit sigClearScreen(); - close(); -} - -void ScreenShot::onCancel() -{ - clearnAndClose(); -} - -void ScreenShot::onFinish() -{ - if (drawToCurrPixmap() && !m_savePixmap.isNull()) - QApplication::clipboard()->setPixmap(m_savePixmap); - clearnAndClose(); -} - -void ScreenShot::onParaBtnId(DrawShape shape, QToolButton* tb) -{ - if (!tb) - return; - - const int idx = tb->objectName().right(1).toInt(); - - const bool bRect = shape == DrawShape::Rectangles; - const bool bEllipses = shape == DrawShape::Ellipses; - const bool bMosaics = shape == DrawShape::Mosaics; - const bool bLine = shape == DrawShape::LineWidth; - const bool bPen = shape == DrawShape::Pen; - const bool bArrows = shape == DrawShape::Arrows; - const bool bText = shape == DrawShape::Text; - const bool bSeriNum = shape == DrawShape::SerialNumber; - - if (bRect || bEllipses || bMosaics) { - setProperty(std::to_string((int)shape).c_str(), idx); - - m_step.bStyele = idx; - } else if (bLine) { - int penWidth = 0; - - if (idx == 0) - penWidth = 1; - else if (idx == 1) - penWidth = 4; - else if (idx == 2) - penWidth = 10; - - m_step.pen.setWidth(penWidth); - - } else if (bPen) { - } else if (bArrows) { - setProperty(std::to_string((int)shape).c_str(), idx); - m_step.bStyele = idx; - } else if (bText) { - } else if (bSeriNum) { - } else { - //XLOG_INFO - } -} - -void ScreenShot::onSelColor(QColor col) -{ - m_step.pen.setColor(col); -} - -// 获取虚拟屏幕截图 -QPixmap* ScreenShot::getVirScrnPixmap() -{ - if (!m_currPixmap) { - QDesktopWidget* desktop = qApp->desktop(); // 获取桌面的窗体对象 - const QRect geom = currentScreen(QCursor::pos())->geometry(); - -#if defined(Q_OS_MAC) - m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), geom.x(), geom.y(), geom.width(), geom.height())); -#else - #ifdef _MYDEBUG - m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), geom.x(), geom.y(), geom.width(), geom.height())); - #else - // 多屏的矩形取并集 - m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), m_virGeom.x(), m_virGeom.y(), m_virGeom.width(), m_virGeom.height())); - #endif -#endif - } - - //m_currPixmap->save("123456.png"); - return m_currPixmap; -} - -bool ScreenShot::drawToCurrPixmap() -{ - if (!m_savePixmap || !m_currPixmap) - return false; - - QPainter pa; - pa.begin(m_currPixmap); - for (XDrawStep& it : m_vDrawed) - drawStep(pa, it); - pa.end(); - - QRect rtSel(m_rtCalcu.getSelRect()); - const QRect rtAppVirDesktop(QPoint(0, 0), m_virGeom.size()); - m_rtCalcu.limitBound(rtSel, rtAppVirDesktop); - if (rtSel.width() > 0 && rtSel.height() > 0) - m_savePixmap = m_currPixmap->copy(QRect(rtSel.topLeft() * getDevicePixelRatio(), rtSel.size() * getDevicePixelRatio())); // 注意独立屏幕缩放比(eg: macox = 2) - - return (!m_savePixmap.isNull() && m_currPixmap); -} - -// 获取已绘画形状的矩形,准备拖曳移动其 -bool ScreenShot::getDrawedShapeRect() -{ - return false; -} - -void ScreenShot::drawBorderMac(QPainter & pa, QRect rt, int num, bool isRound) -{ - if (num == 0) - return; - - pa.save(); - pa.setRenderHint(QPainter::Antialiasing, false); - pa.setBrush(Qt::NoBrush); - QPen penWhite(QColor(255, 255, 255, 1 * 255), 1); - penWhite.setStyle(Qt::CustomDashLine); - penWhite.setDashOffset(0); - penWhite.setDashPattern(QVector() << 4 * ScreenShot::getScale() << 4 * ScreenShot::getScale()); - penWhite.setCapStyle(Qt::FlatCap); - pa.setPen(penWhite); - pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.right(), rt.top())); - pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.left(), rt.bottom())); - pa.drawLine(QPoint(rt.left(), rt.bottom()), QPoint(rt.right(), rt.bottom())); - pa.drawLine(QPoint(rt.right(), rt.top()), QPoint(rt.right(), rt.bottom())); - - QPen penBlack(penWhite); - penBlack.setColor(QColor(0, 0, 0, 1 * 255)); - penBlack.setDashOffset(4); - pa.setPen(penBlack); - pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.right(), rt.top())); - pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.left(), rt.bottom())); - pa.drawLine(QPoint(rt.left(), rt.bottom()), QPoint(rt.right(), rt.bottom())); - pa.drawLine(QPoint(rt.right(), rt.top()), QPoint(rt.right(), rt.bottom())); - - int x1 = 0; - int y1 = 0; - int x2 = 0; - int y2 = 0; - rt.getCoords(&x1, &y1, &x2, &y2); - - QVector ver = { QPoint(x1, y1), - QPoint(x2, y1), - QPoint(x1, y2), - QPoint(x2, y2), - QPoint((x1 + x2) / 2.0, y1), - QPoint((x1 + x2) / 2.0, y2), - QPoint(x1, (y1 + y2) / 2.0), - QPoint(x2, (y1 + y2) / 2.0) }; - - pa.setPen(QPen(Qt::white, 1.5)); - pa.setBrush(QColor(146, 146, 146, 1 * 255)); - QPoint offsetPos(HAIF_R_BORDER_MARK * ScreenShot::getScale(), HAIF_R_BORDER_MARK * ScreenShot::getScale()); - pa.setRenderHint(QPainter::Antialiasing, true); - - for (int i = 0; i < num; ++i) - pa.drawEllipse(ver[i], offsetPos.x(), offsetPos.y()); - - pa.restore(); -} - -void ScreenShot::drawBorderPS(QPainter& pa, QRect rt, bool isRound) -{ - pa.save(); - pa.setRenderHint(QPainter::Antialiasing, true); - QPen pen; - pen.setWidth(XHelper::instance().borderWidth() * m_scal + SELECT_ASSIST_RECT_PEN_WIDTH); - pen.setColor(XHelper::instance().borderColor()); // - pa.setPen(pen); - pa.setBrush(Qt::NoBrush); - - int x1 = rt.left(); - int y1 = rt.top(); - int x2 = rt.right(); - int y2 = rt.bottom(); - - const int penWidth = pen.width(); - const int penAssWidth = SELECT_ASSIST_RECT_WIDTH * m_scal; - - // hor 且补齐交叉角落的空缺的那一块 - QLine l1(QPoint(x1 - penWidth / 2, y1), QPoint(x1 + penAssWidth, y1)); - QLine l2(QPoint(x1 - penWidth / 2, y2), QPoint(x1 + penAssWidth, y2)); - QLine l3(QPoint(x2 + penWidth / 2, y1), QPoint(x2 - penAssWidth, y1)); - QLine l4(QPoint(x2 + penWidth / 2, y2), QPoint(x2 - penAssWidth, y2)); - - // ver - QLine l5(QPoint(x1, y1), QPoint(x1, y1 + penAssWidth)); - QLine l6(QPoint(x1, y2), QPoint(x1, y2 - penAssWidth)); - QLine l7(QPoint(x2, y1), QPoint(x2, y1 + penAssWidth)); - QLine l8(QPoint(x2, y2), QPoint(x2, y2 - penAssWidth)); - - pa.drawLine(l1.translated(QPoint(0, -penWidth / 2))); - pa.drawLine(l2.translated(QPoint(0, penWidth / 2))); - pa.drawLine(l3.translated(QPoint(0, -penWidth / 2))); - pa.drawLine(l4.translated(QPoint(0, penWidth / 2))); - pa.drawLine(l5.translated(QPoint(-penWidth / 2, 0))); - pa.drawLine(l6.translated(QPoint(-penWidth / 2, 0))); - pa.drawLine(l7.translated(QPoint(penWidth / 2, 0))); - pa.drawLine(l8.translated(QPoint(penWidth / 2, 0))); - - pen.setWidth(XHelper::instance().borderWidth() * m_scal); - pa.setPen(pen); - pa.drawRect(rt); - pa.restore(); -} - -// 绘画当前类型的一个图案形状; isUseOwn 为 true 使用自带的画笔等;true 使用上一个环境的 -void ScreenShot::drawStep(QPainter& pa, XDrawStep& step, bool isUseEnvContext) -{ - if (DrawShape::NoDraw == step.shape - || (m_pCurrShape == &step && m_rtCalcu.scrnType == ScrnType::Move)) // 选中图形的处于移动状态,跳过这不绘画 - return; - - //pa.save(); - //pa.setRenderHint(QPainter::SmoothPixmapTransform, true); - //pa.setRenderHint(QPainter::HighQualityAntialiasing, true); - pa.setRenderHint(QPainter::Antialiasing, true); - - if (!isUseEnvContext) { - if (step.bStyele == 0){ - //step.brush.setColor(Qt::NoBrush); - step.brush.setStyle(Qt::NoBrush); - } else if (step.bStyele == 1) { - step.brush.setColor(step.pen.color()); - step.brush.setStyle(Qt::SolidPattern); - } - - pa.setPen(step.pen); - pa.setBrush(step.brush); - pa.setFont(step.font); - } - - switch (step.shape) { - case DrawShape::Rectangles: { - if (step.rt.isEmpty()) - break; - - pa.drawRect(step.rt); - break; - } - case DrawShape::Ellipses: { - if (step.rt.isEmpty()) - break; - - pa.drawEllipse(step.rt.center(), step.rt.width() / 2, step.rt.height() / 2); - break; - } - case DrawShape::Arrows: { - step.brush.setColor(step.pen.color()); // TODO 2022.01.16: 可以优化到上面去的 if 判断 - step.brush.setStyle(Qt::SolidPattern); - pa.setBrush(step.brush); - - if (step.bStyele == 0) { - pa.drawLine(step.p1, step.p2); - } else if (step.bStyele == 1 || step.bStyele == 2) { - pa.drawLine(XHelper::instance().GetShorterLine(step.p1, step.p2)); // 0, 1, 2 - QPainterPath arrowPath = XHelper::instance().GetArrowHead(step.p1, step.p2); - pa.fillPath(arrowPath, step.brush); - } - break; - } - case DrawShape::Pen: { - - pa.drawPolyline(step.custPath.data(), step.custPath.size()); - break; - } - case DrawShape::Text: { - // 记住:这是每一个 step 都会绘画的 - if (step.bTextComplete && !step.bDisplay && step.text.size()) { - QFontMetrics metrics(m_textEdit->font()); - - //QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); - //QPoint pos(step.editPos.x(), step.editPos.y() + metrics.ascent() + metrics.leading()); // 偏移得到视觉的“正确” - - int flags = Qt::TextWordWrap; // 自动换行 - QRect textBoundingRect = metrics.boundingRect(QRect(0, 0, m_virGeom.width(), 0), flags, step.text); - - //http://qtdebug.com/qtbook-paint-text - //https://doc.qt.io/qt-5/qpainter.html#drawText-5 - //const QFont font(pa.font()); - - qDebug() << step.font.pointSize(); - pa.setFont(step.font); - pa.drawText(QRect(step.editPos, textBoundingRect.size()), flags, step.text); - //pa.setFont(font); - } - break; - } - case DrawShape::Mosaics: { - if (!m_currPixmap || step.rt.isEmpty()) // 优化,删除就很明显 - return; - - QPixmap mosaicPixmap = m_currPixmap->copy(QRect(mapFromGlobal(step.rt.topLeft()) * getDevicePixelRatio(), step.rt.size() * getDevicePixelRatio())); - if (step.bStyele == 0) { - const QPixmap* pix = XHelper::instance().SetMosaicSmooth(&mosaicPixmap, step.mscPx); - pa.drawPixmap(step.rt, *pix); - } else if (step.bStyele == 1) { - const QImage img = XHelper::instance().SetMosaicPixlelated(&mosaicPixmap, step.mscPx); - pa.drawImage(step.rt, img); - } - - break; - } - default: - break; - } - - //pa.restore(); -} - -bool ScreenShot::isDrawShape(XDrawStep& step) -{ - if (step.p1 == step.p2) - return false; - - //if (step.shape == DrawShape::Rectangles) { - //} else if (step.shape == DrawShape::Ellipses) { - - //} else if (step.shape == DrawShape::Pen) { - - //} else if (step.shape == DrawShape::Arrows) { - - //} else if (step.shape == DrawShape::Mosaics) { - - //} else if (step.shape == DrawShape::Text || step.shape == DrawShape::NoDraw) { - // return false; // 特殊处理,或者不处理 - //} else { - // return true; // 避免警告 - //} - - return true; -} - -const QVector ScreenShot::drawBarPosition(Qt::Orientation orien /*= Qt:: Horizonta*/, ToolBarOffset offset /*= ToolBarOffset::TBO_Middle*/) -{ - QVector vec; - if (!m_selBar || !m_paraBar) - return vec; - - const int space = B_SPACEING; - int selBarHeight = m_selBar->height(); - int paraBarHeight = m_paraBar->height(); - - if (orien == Qt::Vertical) { - selBarHeight = m_selBar->width(); - paraBarHeight = m_paraBar->width(); - } - - const int sBarHeight = space + selBarHeight; - const int pBarHeight = space + paraBarHeight; - const QRect rtSel(m_rtCalcu.getSelRect()); - const int barMaxTop = rtSel.top() - sBarHeight; - const int barMaxBottom = rtSel.bottom() + sBarHeight; - const int barMaxLeft = rtSel.left() - sBarHeight; - const int barMaxRight = rtSel.right() + sBarHeight; - - -// QDesktopWidget* desktop = QApplication::desktop(); // 获取桌面的窗体对象 -// QRect rtScrn = desktop->screen(desktop->screenNumber(rtSel.bottomRight() - QPoint(0, 1)))->geometry(); // geometry 则左上角坐标非 0,0; (0, 1) 为修正底部置底后, 返回为(错的)另一个显示器 - QScreen* curScrn = qGuiApp->screenAt(rtSel.bottomRight()); - if (!curScrn) - curScrn = qGuiApp->screenAt(rtSel.topRight()); - if (!curScrn) - curScrn = qGuiApp->screenAt(QCursor::pos()); - - QRect rtScrn = curScrn->geometry(); // 取代上面的那个,但有个 bug,光标在底部或者之外,返回 nullptr - QPoint p1; // selBar - QPoint p2; // paraBar - if (orien == Qt::Horizontal) { - p1 = QPoint(rtSel.center().x() - m_selBar->width() / 2, rtSel.bottom() + space); // 默认底部中间 - p2 = QPoint(p1.x(), p1.y() + space + selBarHeight); - - if (offset == ToolBarOffset::TBO_Left) - p1.setX(rtSel.left()); - else if (offset == ToolBarOffset::TBO_Right) - p1.setX(rtSel.right() - m_selBar->width()); - - int topLimit = qMax(m_virGeom.top(), rtScrn.top()); - int bottomLimit = qMin(m_virGeom.bottom(), rtScrn.bottom()); - - if (barMaxTop > topLimit) { // 选中框,上未触顶 - if (barMaxBottom > bottomLimit) { // 底部触顶 - p1.setY(rtSel.top() - sBarHeight); - p2.setY(p1.y() - pBarHeight); - } - - if (m_paraBar->isVisible()) { - if (barMaxBottom + pBarHeight > bottomLimit) { - - if (barMaxTop - pBarHeight < topLimit) { - p1.setY(rtSel.top() + space); - p2.setY(rtSel.top() + sBarHeight + space); - } else { - p1.setY(rtSel.top() - sBarHeight); - p2.setY(p1.y() - pBarHeight); - } - } else { - p1.setY(rtSel.bottom() + space); - } - } - - } else { - if (barMaxBottom > bottomLimit - || (m_paraBar->isVisible() && barMaxBottom + pBarHeight > bottomLimit)) { // 底部触顶 - p1.setY(rtSel.top() + space); - p2.setY(rtSel.top() + sBarHeight + space); - } - } - - } else { - - p1 = QPoint(rtSel.left() - (selBarHeight + space), rtSel.center().y() - m_selBar->height() / 2); // 默认左侧中间 - p2 = QPoint(p1.x() - space * 2 - selBarHeight, p1.y()); - if (offset == ToolBarOffset::TBO_Top) - p1.setY(rtSel.top()); - else if (offset == ToolBarOffset::TBO_Bottom) - p1.setY(rtSel.bottom() - m_selBar->height()); - - int leftLimit = qMax(m_virGeom.left(), rtScrn.left()); - int rightLimit = qMin(m_virGeom.right(), rtScrn.right()); - - if (barMaxLeft > leftLimit) { // 选中框左边未触顶 - //if (barMaxRight > rightLimit) // 右侧触顶 - // basePos.setY(rtSel.bottom() - barHeight); - } else { - p1.setX(rtSel.right() + space); - p2.setX(p1.x() + space + selBarHeight); - - if (barMaxRight > rightLimit) { // 右侧触顶 - p1.setX(rtSel.left() + space); - p2.setX(p1.x() + space + selBarHeight); - } - } - - p2.setY(p1.y()); - } - - vec << p1 << p2; - return vec; -} - -const QPoint ScreenShot::drawSelSizePosition(const QRect rt) -{ - QPoint ret; - if (!m_selSize) - return ret; - - //QString str = QString("rtSel(%1, %2, %3 * %4) m_savePixmap.rect:%5 * %6").arg(rt.left()).arg(rt.top()).arg(rt.width()).arg(rt.height()) - // .arg(m_savePixmap.width()).arg(m_savePixmap.height()); - - QString str = QString("%1 x %2").arg(rt.width()).arg(rt.height()); - - if (str.compare(m_selSize->text()) != 0) { - emit m_selSize->sigTextChanged(str); - m_selSize->setText(str); - } - - ret.setX(rt.left()); - ret.setY(rt.top() - m_selSize->height() - SS_SPACE_TO_SELECTRECT); - return ret; -} - -void ScreenShot::selectedShapeMove(QPainter& pa) -{ - QRect rtCurMove; - if (m_pCurrShape && (m_rtCalcu.scrnType == ScrnType::Wait || m_rtCalcu.scrnType == ScrnType::Move)) { - pa.save(); - - rtCurMove = m_pCurrShape->rt.translated(m_rtCalcu.pos2 - m_rtCalcu.pos1); - pa.setRenderHint(QPainter::Antialiasing, false); - pa.setBrush(Qt::NoBrush); - QPen penWhite(QColor(255, 255, 255, 1 * 255), 1); - penWhite.setStyle(Qt::CustomDashLine); - penWhite.setDashOffset(0); - penWhite.setDashPattern(QVector() << 4 * ScreenShot::getScale() << 4 * ScreenShot::getScale()); - penWhite.setCapStyle(Qt::FlatCap); - pa.setPen(penWhite); - - const int r = 3; - QPen penBlack(penWhite); - penBlack.setColor(QColor(0, 0, 0, 1 * 255)); - penBlack.setDashOffset(4); - - if (m_pCurrShape->shape == DrawShape::Rectangles) { - pa.setBrush(m_pCurrShape->brush); - pa.setPen(m_pCurrShape->pen); - pa.drawRect(rtCurMove); - - pa.setBrush(Qt::NoBrush); - pa.setPen(penWhite); - pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); - pa.setPen(penBlack); - pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); - } else if (m_pCurrShape->shape == DrawShape::Ellipses) { - pa.setBrush(m_pCurrShape->brush); - pa.setPen(m_pCurrShape->pen); - pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); - - pa.setBrush(Qt::NoBrush); - pa.setPen(penWhite); - pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); - pa.setPen(penBlack); - pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); - - //pa.setBrush(Qt::NoBrush); - //pa.setPen(penWhite); - - //// 生成可填充的轮廓 - //QPainterPathStroker stroker; - //stroker.setCapStyle(Qt::RoundCap); // 端点风格 - //stroker.setJoinStyle(Qt::RoundJoin); // 连接样式 - //stroker.setDashPattern(Qt::DashLine); // 虚线图案 - //stroker.setWidth(penWhite.width()); // 宽度 - - //// 生成一个新路径(可填充区域),表示原始路径 path 的轮廓 - //QPainterPath outlinePath = stroker.createStroke(path); - - - //pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); - //pa.setPen(penBlack); - //pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); - } - - pa.restore(); - } -} -void ScreenShot::whichShape() -{ - bool bChecked = false; // 点击是否选中 - const int r = 4; - QPoint pos(QCursor::pos()); - for (auto it = m_vDrawed.rbegin(); it != m_vDrawed.rend(); ++it) { - const auto& rt = it->rt; - const QRect& rtOut = rt.adjusted(-r, -r, r, r); - const QRect& rtIn = rt.adjusted(r, r, -r, -r); - - QPainterPath path; - path.setFillRule(Qt::OddEvenFill); - if (it->shape == DrawShape::Rectangles) { - path.addEllipse(rtOut); - if (it->bStyele == 0) - path.addEllipse(rtIn); - } else if (it->shape == DrawShape::Ellipses) { - - path.addEllipse(rtOut.center(), rtOut.width() / 2, rtOut.height() / 2); - if (it->bStyele == 0) - path.addEllipse(rtIn.center(), rtIn.width() / 2, rtIn.height() / 2); - } else if (it->shape == DrawShape::Arrows) { - // TODO 2022.07.12: 倾斜的矩形 + 三角形; 旋转坐标轴后绘画? - } - - if (path.contains(pos)) { - m_pCurrShape = &(*it); - bChecked = true; - return; - } - } - - m_pCurrShape = nullptr; // 没有选中任何一个 -} - -void ScreenShot::savePixmap(bool quickSave /*= true*/, bool autoSave /*= true*/) -{ - if (quickSave) { - //auto path = XHelper::instance().path(toQuickSavePath); - } -} - -// 样式一: 浅蓝色 -void ScreenShot::drawBorderBlue(QPainter& pa, QRect rt, int num, bool isRound) -{ - if (num == 0) - return; - - pa.setPen(Qt::NoPen); - pa.setBrush(Qt::NoBrush); - - QIcon icon(":/resources/icons/boardPoint_8px.svg"); - QPixmap pixmap = icon.pixmap(QSize(HAIF_R_BORDER_MARK, HAIF_R_BORDER_MARK) * 4 * ScreenShot::getScale()); - pixmap.setDevicePixelRatio(getDevicePixelRatio()); - - QPoint offsetPos(HAIF_R_BORDER_MARK * 2 * ScreenShot::getScale(), HAIF_R_BORDER_MARK * 2 * ScreenShot::getScale()) ; - pa.drawPixmap(rt.topLeft() - offsetPos, pixmap); - pa.drawPixmap(rt.topRight() - offsetPos, pixmap); - pa.drawPixmap(rt.bottomLeft() - offsetPos, pixmap); - pa.drawPixmap(rt.bottomRight() - offsetPos, pixmap); - - if (num <= 8) { - int x1 = 0; - int y1 = 0; - int x2 = 0; - int y2 = 0; - - rt.getCoords(&x1, &y1, &x2, &y2); - pa.drawPixmap(QPoint((x1 + x2) / 2, y1) - offsetPos, pixmap); - pa.drawPixmap(QPoint((x1 + x2) / 2, y2) - offsetPos, pixmap); - pa.drawPixmap(QPoint(x1, (y1 + y2) / 2) - offsetPos, pixmap); - pa.drawPixmap(QPoint(x2, (y1 + y2) / 2) - offsetPos, pixmap); - } -} - -void ScreenShot::showAllDrawedShape(QPainter& pa) -{ - QPoint posText(0, 100); - const int space = 15; -// QRect m_rtCalcu_selRect(m_rtCalcu.getSelRect()); - - int i = 0; - pa.drawText(posText + QPoint(0, space * i), QString("m_vDrawed:%0").arg(m_vDrawed.count())); - - //for (const auto& it : m_vDrawed){ - // pa.drawText(posText + QPoint(0, space * i), QString("m_vDrawed[%0]:[%1]") - // .arg(i++).arg(it.shape)); - //} - - //pa.drawText(posText + QPoint(0, space * 1), QString("pos1: (%1, %2) pos2: (%3, %4)") - // .arg(m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos1.y()).arg(m_rtCalcu.pos2.x()).arg(m_rtCalcu.pos2.y())); - //pa.drawText(posText + QPoint(0, space * 2), QString("pos2 - pos1:(%1, %2)") - // .arg(m_rtCalcu.pos2.x() - m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos2.y() - m_rtCalcu.pos1.y())); - //pa.drawText(posText + QPoint(0, space * 3), QString("m_rtCalcu.getSelRect(): (%1, %2, %3 * %4)") - // .arg(m_rtCalcu_selRect.x()).arg(m_rtCalcu_selRect.y()).arg(m_rtCalcu_selRect.width()).arg(m_rtCalcu_selRect.height())); - //pa.drawText(posText + QPoint(0, space * 4), QString("rtSel: (%1, %2, %3 * %4)") - // .arg(rtSel.x()).arg(rtSel.y()).arg(rtSel.width()).arg(rtSel.height())); - //pa.drawText(posText + QPoint(0, space * 5), QString("pos(): (%1, %2)") - // .arg(pos().x()).arg(pos().y())); - //pa.drawText(posText + QPoint(0, space * 6), QString("m_rtAtuoMonitor: (%1, %2), rtAtuoMonitor: (%3, %4)") - // .arg(m_rtAtuoMonitor.x()).arg(m_rtAtuoMonitor.y()).arg(rtAtuoMonitor.x()).arg(rtAtuoMonitor.y())); - //pa.drawText(posText + QPoint(0, space * 7), QString("m_rtCalcu.bSmartMonitor: %1") - // .arg(m_rtCalcu.bSmartMonitor)); -} - -// 效果:绘画的顺序重要 -void ScreenShot::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - if (m_rtCalcu.scrnType == ScrnType::Draw) - setCursor(Qt::CrossCursor); - - // 原始图案 - QPainter pa(this); // 改为始终从 (0, 0) 开始绘画 - pa.setBrush(Qt::NoBrush); - pa.setPen(Qt::NoPen); - if (m_currPixmap) - pa.drawPixmap(QPoint(0, 0), *m_currPixmap); - - // 选中矩形图片 - QRect rtSel(m_rtCalcu.getSelRect()); // 移动选中矩形 - if (m_bSmartWin) - rtSel = m_rtSmartWindow; - - const auto & virGeom = QRect(mapFromGlobal(m_virGeom.topLeft()), m_virGeom.size()); // 修复为相对窗口的 - m_rtCalcu.limitBound(rtSel, virGeom); // 修复边界时图片被拉伸 - if (rtSel.width() > 0 && rtSel.height() > 0) { - m_savePixmap = m_currPixmap->copy(QRect(rtSel.topLeft() * getDevicePixelRatio(), rtSel.size() * getDevicePixelRatio())); // 注意独立屏幕缩放比(eg: macox = 2) - pa.drawPixmap(rtSel, m_savePixmap); - - // 放大镜实现 - //QSize tSize(100, 100); - //auto& mousePos = QCursor::pos(); - ////QRect rtMagnifying(QPoint(mousePos.x() - tSize.width() / 2, mousePos.y() - tSize.height() / 2), tSize); - //QRect rtPick(mousePos * getDevicePixelRatio(), tSize * getDevicePixelRatio()); - //pa.drawPixmap(mousePos + QPoint(100, 100), m_currPixmap->copy(rtPick).scaled(tSize * 4, Qt::KeepAspectRatio)); // 放大 4 倍 - - // m_savePixmap 和 m_currPixmap 的地址没有改变,但前者的 cacheKey 总在变化??? - } - - // 画家准备 - pa.setRenderHint(QPainter::Antialiasing, true); - const int penWidth = HAIF_INTERVAL; // 画笔宽度 - QPen pen(QColor("#01bdff")); - pen.setWidth(penWidth); - pa.setPen(pen); - pa.setOpacity(1); - pa.setBrush(Qt::transparent); - - // 绘画图案 - for (XDrawStep& it : m_vDrawed) - drawStep(pa, it); - - // 绘画当前步 - pen.setWidth(penWidth / 2); - pen.setColor(Qt::green); - pa.setPen(pen); - drawStep(pa, m_step, false); - - //int i = 0; - //// test 所有绘画的中那个 - //for (const WinData& it : IWinInfo::m_vWinData) { - // //const auto pen = pa.pen(); - // //pa.setPen(QPen(Qt::black, 10)); - // - // if (!it.bFilter) { - // const QRect& t = it.rect; - // int m = 10; - // pa.drawRect(t); - - // pa.drawText(QPoint(t.topLeft()) + QPoint(0, 30), it.path); - // pa.drawText(QPoint(t.topLeft()) + QPoint(0, 10), it.title + QString("[%1]").arg(i++)); - // } - - // //pa.setPen(pen); - //} - - selectedShapeMove(pa); // flameshot 选中图形的效果 - - // 屏幕遮罩 - QPainterPath path; - path.addRect(virGeom); - path.addRect(rtSel); - path.setFillRule(Qt::OddEvenFill); - pa.setPen(Qt::NoPen); - pa.setBrush(QColor(0, 0, 0, 0.5 * 255)); - pa.drawPath(path); - - // 边框 - if (rtSel.width() > 0 && rtSel.height() > 0){ - pen.setStyle(Qt::SolidLine); - pa.setBrush(Qt::NoBrush); - pa.setPen(QPen(XHelper::instance().borderColor(), XHelper::instance().borderWidth())); - m_selSize->move(drawSelSizePosition(rtSel)); - - // 绘画边框样式 - const int styleIndex = XHelper::instance().boardStyle(); - if (styleIndex == 1) { - drawBorderPS(pa, rtSel); - } else if (styleIndex == 2) { - drawBorderMac(pa, rtSel); - } else if (styleIndex == 3) { - pa.drawRect(rtSel); - drawBorderBlue(pa, rtSel); - } - } - - // 绘画工具栏 - if (isVisible() && m_selBar && m_bFirstSel) { - const auto v = drawBarPosition(m_barOrien, ToolBarOffset::TBO_Middle); - - if (v.size() == 2){ - m_selBar->move(v.at(0)); - m_paraBar->move(v.at(1)); - } - - // 添加磨砂透明效果 - auto t1 = m_currPixmap->copy(QRect(v.at(0) * getDevicePixelRatio(), m_selBar->rect().size() * getDevicePixelRatio())); - m_selBar->setBlurBackground(t1, 7); - auto t2 = m_currPixmap->copy(QRect(v.at(1) * getDevicePixelRatio(), m_paraBar->rect().size() * getDevicePixelRatio())); - m_paraBar->setBlurBackground(t2, 7); - } - - // 绘画十字线 - drawCrosshair(pa); - - //#ifdef _DEBUG 调试信息 - // 构造函数有偏移 geom.topLeft(); 绘画时候要偏移回来 - // TODO 2022.01.28: 要屏蔽部分窗口;尤其那个 "设置窗口名称的",还要做一下区分 - pen.setColor(Qt::red); - pa.setPen(pen); - pa.setBrush(Qt::NoBrush); - - //pen.setColor(Qt::yellow); - //pa.setPen(pen); - //QRect rtAtuoMonitor(m_rtAtuoMonitor); // 特地使用副本,保留原来数值避免被修改,也是按下和松开判断时候数据保持一致 - //if (m_rtCalcu.bSmartMonitor) { - // - // for (const auto & it : m_vWholeScrn) { - // if (it == rtAtuoMonitor) - // rtAtuoMonitor.adjust(10, 10, -10, -10); - // } - - // pa.drawRect(rtAtuoMonitor); - //} - - pen.setColor(Qt::white); - pa.setPen(pen); - - -#ifdef _MYDEBUG - // 调试的实时数据 - QFont font;//(font()); - font.setPointSize(12); // 默认大小为 9 - pa.setFont(font); - const int space = font.pointSize() * 2.5; - - QPoint tTopLeft; - tTopLeft.setX(m_priScrn->geometry().x()); - tTopLeft.setY(m_priScrn->size().height()); - - QPoint tPosText(tTopLeft.x(), tTopLeft.y() - 5 * space); - QRect m_rtCalcu_selRect(m_rtCalcu.getSelRect()); - - pa.drawText(tPosText - QPoint(0, space * -1), QString("Wait(0) Select(1) Move(2) Draw(3) Stretch(4)")); - pa.drawText(tPosText - QPoint(0, space * 0), QString("m_rtCalcu.scrnType: %1") - .arg(int(m_rtCalcu.scrnType))); - pa.drawText(tPosText - QPoint(0, space * 1), QString("pos1: (%1, %2) pos2: (%3, %4)") - .arg(m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos1.y()).arg(m_rtCalcu.pos2.x()).arg(m_rtCalcu.pos2.y())); - pa.drawText(tPosText - QPoint(0, space * 2), QString("pos2 - pos1:(%1, %2)") - .arg(m_rtCalcu.pos2.x() - m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos2.y() - m_rtCalcu.pos1.y())); - pa.drawText(tPosText - QPoint(0, space * 3), QString("m_rtCalcu.getSelRect(): (%1, %2, %3 * %4)") - .arg(m_rtCalcu_selRect.x()).arg(m_rtCalcu_selRect.y()).arg(m_rtCalcu_selRect.width()).arg(m_rtCalcu_selRect.height())); - pa.drawText(tPosText - QPoint(0, space * 4), QString("rtSel: (%1, %2, %3 * %4)") - .arg(rtSel.x()).arg(rtSel.y()).arg(rtSel.width()).arg(rtSel.height())); - pa.drawText(tPosText - QPoint(0, space * 5), QString("pos(): (%1, %2)") - .arg(pos().x()).arg(pos().y())); - pa.drawText(tPosText - QPoint(0, space * 6), QString("m_rtAtuoMonitor: (%1, %2, %3 * %4)") - .arg(m_rtSmartWindow.x()).arg(m_rtSmartWindow.y()).arg(m_rtSmartWindow.width()).arg(m_rtSmartWindow.height())); - pa.drawText(tPosText - QPoint(0, space * 7), QString("XHelper::instance().smartWindow(): %1") - .arg(XHelper::instance().smartWindow())); - pa.drawText(tPosText - QPoint(0, space * 8), QString("m_vDrawed:%1").arg(m_vDrawed.count())); - if (m_pCurrShape) { - QRect rtCurMove = m_pCurrShape->rt.translated(m_rtCalcu.pos2 - m_rtCalcu.pos1); - pa.drawText(tPosText - QPoint(0, space * 9), QString("rtMoveTest(%1, %2, %3 * %4)").arg(rtCurMove.x()).arg(rtCurMove.y()) - .arg(rtCurMove.width()).arg(rtCurMove.height())); - } - pa.drawText(tPosText - QPoint(0, space * 10), QString("m_step=>pos1(%1, %2) pos2(%3 * %4) editPos(%5, %6) rt(%7, %8, %9 * %10) text:%11") - .arg(m_step.p1.x()).arg(m_step.p1.y()).arg(m_step.p2.x()).arg(m_step.p2.y()) - .arg(m_step.editPos.x()).arg(m_step.editPos.y()) - .arg(m_step.rt.x()).arg(m_step.rt.y()).arg(m_step.rt.width()).arg(m_step.rt.height()) - .arg(m_step.text)); - - const int tSpace = 10; - const int barHeight = m_selBar->height(); - //const QRect rtSel(m_rtCalcu.getSelRect()); - QPoint topLeft(rtSel.right() - m_selBar->width(), rtSel.bottom() + tSpace); - const int barMaxTop = rtSel.top() - tSpace - barHeight; - const int barMaxBottom = rtSel.bottom() + tSpace + barHeight; - - QDesktopWidget* desktop = QApplication::desktop(); // 获取桌面的窗体对象 - //QRect rtScrn = desktop->screen(desktop->screenNumber(rtSel.bottomRight()))->geometry(); // geometry 则左上角坐标非 0,0 - QRect rtScrn = m_scrns.at(desktop->screenNumber(rtSel.bottomRight()))->geometry(); - // QRect rtScrn = currentScreen(rtSel.bottomRight())->geometry(); // 使用此替换有个 bug, 不在时候返回空 - int topLimit = qMax(m_virGeom.top(), rtScrn.top()); - int bottomLimit = qMin(m_virGeom.bottom(), rtScrn.bottom()); - - pa.drawText(tPosText - QPoint(0, space * 11), QString("barMaxTop:%1 barMaxBottom:%2 m_rtVirDesktop 左上右下(%3, %4, %5 * %6)") - .arg(barMaxTop).arg(barMaxBottom).arg(m_virGeom.left()).arg(m_virGeom.top()).arg(m_virGeom.right()).arg(m_virGeom.bottom())); - pa.drawText(tPosText - QPoint(0, space * 12), QString("rtScrn 左上右下(%1, %2, %3 * %4) topLimit:%5 bottomLimit:%6") - .arg(rtScrn.left()).arg(rtScrn.top()).arg(rtScrn.right()).arg(rtScrn.bottom()).arg(topLimit).arg(bottomLimit)); -#endif - -#if 0 - QRect rtOuter = m_rtCalcu.getExteRect(rtSel); - QRect rtInner = m_rtCalcu.getInteRect(rtSel); - int interval = (rtOuter.height() - rtInner.height()) / 2; - - QRect rtLeft(rtOuter.left(), rtInner.top(), interval, rtInner.height()); - QRect rtTop(rtInner.left(), rtOuter.top(), rtInner.width(), interval); - QRect rtRight(rtInner.right(), rtInner.top(), interval, rtInner.height()); - QRect rtBottom(rtInner.left(), rtInner.bottom(), rtInner.width(), interval); - QRect rtTopLeft(rtOuter.left(), rtOuter.top(), interval, interval); - QRect rtTopRight(rtInner.right(), rtOuter.top(), interval, interval); - QRect rtBottomLeft(rtOuter.left(), rtInner.bottom(), interval, interval); - QRect rtBottomRight(rtInner.right(), rtInner.bottom(), interval, interval); - - //pa.setBrush(Qt::blue); - - pa.setBrush(Qt::NoBrush); - pa.setPen(Qt::blue); - pa.drawRect(rtLeft); - pa.drawRect(rtTop); - pa.drawRect(rtRight); - pa.drawRect(rtBottom); - //pa.setBrush(Qt::yellow); - pa.setPen(Qt::white); - pa.drawRect(rtTopLeft); - pa.drawRect(rtTopRight); - pa.drawRect(rtBottomLeft); - pa.drawRect(rtBottomRight); - - /*qDebug() << "【paintEvent】 :" << m_rtCalcu.scrnType << m_rtCalcu.getSelRect() << rtSel << m_rtCalcu.getSelRect() << " " << m_rtCalcu.selEndPos << " " << m_basePixmap << " " << QRect();*/ - //<< "外部矩形:" << rtOuter << "内部矩形:" << rtInner; -#endif // 1 -} - -void ScreenShot::drawCrosshair(QPainter& pa) -{ - if (XHelper::instance().crosshair() && !m_bFirstPress) { - pa.save(); - pa.setPen(QPen(XHelper::instance().crosshairColor(), XHelper::instance().crosshairWidth())); - pa.setBrush(Qt::NoBrush); - - QPoint p(QCursor::pos()); - QLine l1(QPoint(m_virGeom.left(), p.y()), QPoint(m_virGeom.right(), p.y())); - QLine l2(QPoint(p.x(), m_virGeom.top()), QPoint(p.x(), m_virGeom.bottom())); - pa.drawLine(l1); - pa.drawLine(l2); - pa.restore(); - } -} - -void ScreenShot::keyReleaseEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) { - qDebug() << "Key_Escape"; - emit sigClearScreen(); - // hide() 和 close() 区别: https://stackoverflow.com/questions/39407564 - //hide(); - close(); // // 销毁再不会有问题,由单例改写为 new 形式了。排查:1. tray 有关,改用 qpushbutton 和 close即可; 2.单例有关,该市建议修改为 new 指针的比较合适 - //deleteLater(); - } -} - -// 注意: 1. 按下、松开时候会切换状态;点击绘画按钮也会切换状态 -// 2. 开启鼠标跟踪时机;点击绘画按钮也会相应开启/关闭 -// 3. mousePressEvent、mouseMoveEvent、mouseReleaseEvent 合成整体来看;以及不忘记绘画按钮的槽函数 -void ScreenShot::mousePressEvent(QMouseEvent *event) -{ - //XLOG_DEBUG("BEGIN m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); - if (event->button() != Qt::LeftButton) - return; - - m_bFirstPress = true; - setMouseTracking(false); - if (m_rtCalcu.getSelRect().isEmpty() && m_rtCalcu.scrnType == ScrnType::Wait) { - //m_rtCalcu.clear(); - - m_rtCalcu.scrnType = ScrnType::Select; - m_rtCalcu.pos1 = event->pos(); - m_rtCalcu.pos2 = m_rtCalcu.pos1; - } else if (m_rtCalcu.scrnType == ScrnType::Draw) { - m_step.p1 = event->pos(); - m_step.p2 = m_rtCalcu.pos1; - - if (m_step.shape == DrawShape::Text) { - QPoint perviousPos = m_step.editPos; // 修改之前的显示坐标 - - m_step.editPos = event->pos(); - m_step.rt = m_textEdit->rect(); - m_step.rt.setTopLeft(m_step.editPos); - - if (m_step.bDisplay) { // 输入框显示中 - //m_step.editPos = event->pos(); // pos1、pos2 松开鼠标时候会重置,而editPos不会 - //m_step.rt = m_textEdit->rect(); - //m_step.rt.setTopLeft(m_step.editPos); - //m_textEdit->move(m_step.editPos); - - if (!m_step.rt.contains(event->pos(), true)) { // 编辑完成 - m_step.bTextComplete = true; - m_step.bDisplay = false; - - m_step.text = m_textEdit->toPlainText(); - m_textEdit->clear(); - - m_step.editPos = perviousPos; - m_vDrawed.push_back(m_step); // 绘画文字为单独处理【暂时特例】 - m_step.idxLevel = m_step.totalIdx++; - } else { // 编辑ing - m_step.bTextComplete = false; - } - } else { // 输入框未显示 - m_step.bDisplay = true; - m_textEdit->clear(); - } - - m_textEdit->move(m_step.editPos); - m_textEdit->setVisible(m_step.bDisplay); - - XLOG_DEBUG("m_textEdit是否显示[{}] event->pos({}, {}) m_step.editPos({}, {}) perviousPos({}, {}) m_textEdit->rect({}, {}, {} * {}) m_textEdit->toPlainText[{}] m_step.text[{}]" - , m_textEdit->isVisible(), event->pos().x(), event->pos().y(), m_step.editPos.x(), m_step.editPos.y(), perviousPos.x(), perviousPos.y() - , m_textEdit->rect().left(), m_textEdit->rect().top(), m_textEdit->rect().width(), m_textEdit->rect().height() - , m_textEdit->toPlainText().toUtf8().data() - , m_step.text.toUtf8().data()); - } - - } else { // 则可能为移动、拉伸、等待状态 - m_rtCalcu.scrnType = updateScrnType(event->pos()); - } - - if (m_rtCalcu.scrnType == ScrnType::Move) { - m_rtCalcu.pos1 = event->pos(); - m_rtCalcu.pos2 = event->pos(); - - whichShape(); // 判定移动选中的已绘图形 - } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { - m_rtCalcu.pos1 = event->pos(); - m_rtCalcu.pos2 = event->pos(); - } - - update(); - //XLOG_DEBUG("END m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); -} - -void ScreenShot::mouseMoveEvent(QMouseEvent *event) -{ - //XLOG_DEBUG("BEGIN m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); -// if (event->button() != Qt::LeftButton) -// return; - - // 此时为 Qt::NoButton - if (m_rtCalcu.scrnType == ScrnType::Wait) { - if (m_bSmartWin) - updateGetWindowsInfo(); - } else if (m_rtCalcu.scrnType == ScrnType::Select) { - m_rtCalcu.pos2 = event->pos(); - m_bSmartWin = false; - //if (m_rtCalcu.pos1 != m_rtCalcu.pos2) - // 不显示 TODO: 2022.02.10 再添加一个变量即可 - } else if (m_rtCalcu.scrnType == ScrnType::Move) { - m_rtCalcu.pos2 = event->pos(); - } else if (m_rtCalcu.scrnType == ScrnType::Draw) { - m_step.p2 = event->pos(); - m_step.editPos = event->pos(); - - m_step.custPath.append(event->pos()); - m_step.rt = RectCalcu::getRect(m_step.p1, m_step.p2); - } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { - m_rtCalcu.pos2 = event->pos(); - } - - updateCursorShape(event->pos()); - update(); - //XLOG_DEBUG("END m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); -} - -void ScreenShot::mouseReleaseEvent(QMouseEvent *event) -{ - //XLOG_DEBUG("BEGIN m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); - if (event->button() != Qt::LeftButton) - return; - - if (m_rtCalcu.scrnType == ScrnType::Wait) { - } else if (m_rtCalcu.scrnType == ScrnType::Select) { - m_rtCalcu.pos2 = event->pos(); - - if (m_rtCalcu.pos1 == m_rtCalcu.pos2) { // 点击到一个点,视作智能检测窗口; 否则就是手动选择走下面逻辑 - m_rtCalcu.setRtSel(m_rtSmartWindow); - } - - m_bSmartWin = false; // 自动选择也结束 - - } else if (m_rtCalcu.scrnType == ScrnType::Move) { - m_rtCalcu.pos2 = event->pos(); - - // 移动选中的图形 - if (m_pCurrShape) { - m_pCurrShape->rt.translate(m_rtCalcu.pos2 - m_rtCalcu.pos1); - } - - } else if (m_rtCalcu.scrnType == ScrnType::Draw) { - m_step.p2 = event->pos(); - m_step.custPath.append(event->pos()); - m_step.rt = RectCalcu::getRect(m_step.p1, m_step.p2); - - // DrawShape::Text 在按下时候单独处理 m_vDrawed.push_back - if (m_step.shape != DrawShape::Text && m_step.shape != DrawShape::NoDraw) { - if (isDrawShape(m_step)) { - m_vDrawed.push_back(m_step); // TODO 2022.01.16 优化: 不必每次(无效得)点击,也都记录一次 - m_step.idxLevel = m_step.totalIdx++; - m_step.clear(); - } - } - - } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { - m_rtCalcu.pos2 = event->pos(); - } - - if (!m_rtCalcu.calcurRsultOnce().isEmpty()) { // 计算一次结果 - m_bFirstSel = true; - if (!m_selBar->isVisible()) - m_selBar->setVisible(true); - - if (!m_selSize->isVisible()) - m_selSize->setVisible(true); - } - - if (m_rtCalcu.scrnType != ScrnType::Draw) { - m_rtCalcu.scrnType = ScrnType::Wait; - setMouseTracking(true); - } - - update(); - //XLOG_DEBUG("END m_rtCalcu.scrnType[{}], event->pos({}, {})", int(m_rtCalcu.scrnType), event->pos().x(), event->pos().y()); -} - -void ScreenShot::wheelEvent(QWheelEvent* event) -{ - // Note: On X11 this value is driver specific and unreliable, use angleDelta() instead - //QPoint numPixels = event->pixelDelta(); - QPoint numDegrees = event->angleDelta() / 8; - QPoint numSteps = numDegrees / 15; - - if (numDegrees.isNull()) - return; - - if (m_step.shape == DrawShape::Text) { - // TODO 2022.07.03: 待优化,需同时调整输入框的大小 - m_step.font.setPointSize(m_step.font.pointSize() + numSteps.y()); - } else { - m_step.pen.setWidth(m_step.pen.width() + numSteps.y()); - if (m_step.pen.width() <= 0) - m_step.pen.setWidth(1); - if (m_step.pen.width() >= 100) - m_step.pen.setWidth(100); - } - - event->accept(); - update(); // TODO 2022.04.27: 此行仅调试用 -} - -void ScreenShot::getScrnShots() -{ - m_vWholeScrn.clear(); - m_vWholeScrn.push_back(m_virGeom); - for (const auto& it : m_scrns) - m_vWholeScrn.push_back(it->geometry()); - -#ifdef Q_OS_WIN - //WinInfoWin::instance().getAllWinInfoCache(); - //if (m_rtCalcu.bSmartMonitor) // 存储所需要全部窗口信息 - //m_vec = WinInfoWin::instance().m_vWinInfo; -#elif defined(Q_OS_MAC) -#elif defined(Q_OS_LINUX) -#endif - - getScrnInfo(); - getVirScrnPixmap(); // 因 QWidget 启动后 事件执行顺序,sizeHint() -> showEvent() -> paintEvent();故全屏 show() 之前先获取桌面截图 - show(); - - if (m_bSmartWin) - updateGetWindowsInfo(); - - // fix: 初次使用全局热键召唤截图窗口,对 Esc 无响应。 考虑跨平台或需参考 https://zhuanlan.zhihu.com/p/161299504 - if (!isActiveWindow()) { - activateWindow(); - //setFocus(); - } -} - -// 屏幕详细参数 -void ScreenShot::getScrnInfo() -{ - XLOG_INFO("---------------QApplication::desktop() Info BEGIN----------------"); - XLOG_INFO("所有可用区域 m_virtualGeometry({}, {}, {} * {})", m_virGeom.left(), m_virGeom.top(), m_virGeom.width(), m_virGeom.height()); - XLOG_INFO("主屏可用区域 m_priScrn->geometry()({}, {}, {} * {})", m_priScrn->geometry().left(), m_priScrn->geometry().top(), m_priScrn->geometry().width(), m_priScrn->geometry().height()); - XLOG_INFO("是否开启虚拟桌面 isVirtualDesktop: {}", m_priScrn->virtualSiblings().size() > 1 ? true : false); // 等价于废弃的 QApplication::desktop()->isVirtualDesktop(); - XLOG_INFO("---------------QApplication::desktop() Info END----------------"); - - XLOG_INFO("---------------m_screens[] Info BEGIN----------------"); - for (const auto& it : m_scrns) { - XLOG_DEBUG("屏幕详细信息:index[{}]", m_scrns.indexOf(it)); - XLOG_DEBUG("size({}, {})", it->size().width(), it->size().height()); - XLOG_DEBUG("geometry({}, {}, {} * {})", it->geometry().left(), it->geometry().top(), it->geometry().width(), it->geometry().height()); - XLOG_DEBUG("availableGeometry({}, {}, {} * {})", it->availableGeometry().left(), it->availableGeometry().top(), it->availableGeometry().width(), it->availableGeometry().height()); - XLOG_DEBUG("scrn->virtualGeometry({}, {}, {} * {})", it->virtualGeometry().left(), it->virtualGeometry().top(), it->virtualGeometry().width(), it->virtualGeometry().height()); - - XLOG_INFO("设备像素比 devicePixelRatio[{}] 制造商 manufacturer[{}] 名称 name[{}]", it->devicePixelRatio(), it->manufacturer().toUtf8().data(), it->name().toUtf8().data()); - XLOG_INFO("序号 serialNumber[{}] 刷新率 refreshRate[{}] 模式 model[{}]", it->serialNumber().toUtf8().data(), it->refreshRate(), it->model().toUtf8().data()); - XLOG_INFO("虚拟几何 virtualGeometry:({}, {}, {} * {}) 缩放比 getScale[{}]", it->availableGeometry().left(), it->availableGeometry().top(), it->availableGeometry().width(), it->availableGeometry().height(), getScale(it)); - XLOG_INFO("物理几何 physicalSize:({} * {}) 大小 size({} * {})", it->physicalSize().width(), it->physicalSize().height(), it->physicalSize().width(), it->physicalSize().height()); - XLOG_INFO("物理 DPI physicalDotsPerInch: {} DPIX: {} DPIY: {} ", it->physicalDotsPerInch(), it->physicalDotsPerInchX(), it->physicalDotsPerInchY()); - XLOG_INFO("逻辑 DPI logicalDotsPerInch: {} DPIX: {} DPIY: {}\n", it->logicalDotsPerInch(), it->logicalDotsPerInchX(), it->logicalDotsPerInchX()); - } - - XLOG_INFO("m_virtualGeometry({}, {}, {} * {})\n", m_virGeom.left(), m_virGeom.top(), m_virGeom.width(), m_virGeom.height()); - XLOG_INFO("---------------m_screens[] Info END----------------"); -} - -double ScreenShot::getDevicePixelRatio() -{ -//#ifdef Q_OS_MAC -// return 2; // TODO 2022.03.11 无奈之举;使用 CMake MACOSX_BUNDLE 则返回的缩放比不正常, return 1 ??? -//#endif - return m_priScrn->devicePixelRatio(); - -} - -double ScreenShot::getDevicePixelRatio(QScreen * screen) -{ - if (!screen) - return 0.0; - else - return screen->devicePixelRatio(); -} - -// 随着光标移动,更新获取桌面所有窗口信息 -void ScreenShot::updateGetWindowsInfo() -{ - WinID winId; - -#ifdef Q_OS_WIN - winId._hWnd = (void *)QWidget::winId(); - -#elif defined(Q_OS_MAC) -#elif defined(Q_OS_LINUX) - winId._xWindow = (unsigned long)0; -#endif - - Util::getRectFromCurrentPoint(winId, m_rtSmartWindow); - m_rtSmartWindow = QRect(mapFromGlobal(m_rtSmartWindow.topLeft()), m_rtSmartWindow.size()); -} - -double ScreenShot::getScale(QScreen * screen) -{ -// or defined(Q_WS_WIN) || defined(Q_WS_X11) -#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) - double scale = screen->logicalDotsPerInch() / 96.0; - if (scale < 1.25) - return 1; - else if (1.25 <= scale && scale < 1.5) - return 1.25; - else if (1.5 <= scale && scale < 1.75) - return 1.5; - else if (1.75 <= scale && scale < 2) - return 1.75; - else if (2 <= scale && scale < 2.25) - return 2; - else if (2.25 <= scale && scale < 2.5) - return 2.25; - else if (2.5 <= scale && scale < 3) - return 2.5; - else if (3 <= scale && scale < 3.5) - return 3; - else if (3.5 <= scale && scale < 4) - return 3.5; - else - return scale; -#elif defined(Q_OS_MAC) - double scale = screen->logicalDotsPerInch() / 72.0; - return scale; -#else - return screen->logicalDotsPerInch() / 96.0; // -#endif -} - -bool ScreenShot::isSelBorder() -{ - if (m_pCurrShape) - return false; - else - return true; -} - -const Qt::Orientation ScreenShot::getBarOrien() const -{ - return m_barOrien; -} - -void ScreenShot::setBarOrien(Qt::Orientation val) -{ - m_barOrien = val; -} - -const QScreen *ScreenShot::currentScreen(const QPoint& pos) -{ - const QScreen* curScrn = qGuiApp->screenAt(pos); - -#if defined(Q_OS_MACOS) - // On the MacOS if mouse position is at the edge of bottom or right sides - // qGuiApp->screenAt will return nullptr, so we need to try to find current - // screen by moving 1 pixel inside to the current desktop area - if (!curScrn && (pos.x() > 0 || pos.y() > 0)) - curScrn = qGuiApp->screenAt(QPoint(pos.x() - 1, pos.y() - 1)); -#endif - - //if (!curScrn) - // curScrn = qGuiApp->primaryScreen(); - - if (!curScrn) - qDebug() << "返回的屏幕为空??"; - - return curScrn; -} +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2021.09.29 + * Description: + ******************************************************************/ +#include "screenshot.h" +#include "../core/xlog.h" +#include "../platform/wininfo.h" +#include "../tool/pin/pinwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Util { + bool getRectFromCurrentPoint(WinID winId, QRect &outRect) + { + const QRect rt = WinInfo::instance().targWinRect(winId, false); + + outRect.setLeft(rt.left()); + outRect.setTop(rt.top()); + outRect.setRight(rt.right()); + outRect.setBottom(rt.bottom()); + return true; + } +} + +ScreenShot::ScreenShot(QWidget *parent) + : QWidget(parent) + , m_scal(XHelper::instance().getScale()) + , m_scrns(qGuiApp->screens()) + , m_priScrn(qGuiApp->primaryScreen()) + , m_virGeom(m_priScrn->virtualGeometry()) + , m_currPixmap(nullptr) + , m_rtCalcu(this) + , m_bSmartWin(XHelper::instance().smartWindow()) + , m_bFirstSel(false) + , m_bFirstPress(false) + , m_pCurrShape(nullptr) + , m_edit(new XTextWidget(this)) + , m_rtSmartWindow(0, 0, 0, 0) + , m_barOrien(Qt::Horizontal) // Horizontal | Vertical + , m_selSize(new SelectSize("test", this)) + , m_selBar(new SelectBar(m_barOrien, this)) + , m_paraBar(new ParameterBar(m_barOrien, this)) +{ + XLOG_INFO("bootUniqueId[{}]", QSysInfo::bootUniqueId().data()); + XLOG_INFO("buildAbi[{}]", QSysInfo::buildAbi().toUtf8().data()); + XLOG_INFO("buildCpuArchitecture[{}]", QSysInfo::buildCpuArchitecture().toUtf8().data()); + XLOG_INFO("currentCpuArchitecture[{}]", QSysInfo::currentCpuArchitecture().toUtf8().data()); + XLOG_INFO("kernelType[{}]", QSysInfo::kernelType().toUtf8().data()); + XLOG_INFO("kernelVersion[{}]", QSysInfo::kernelVersion().toUtf8().data()); + XLOG_INFO("machineHostName[{}]", QSysInfo::machineHostName().toUtf8().data()); + XLOG_INFO("machineUniqueId[{}]", QSysInfo::machineUniqueId().data()); + XLOG_INFO("prettyProductName[{}]", QSysInfo::prettyProductName().toUtf8().data()); + XLOG_INFO("productType[{}]", QSysInfo::productType().toUtf8().data()); + XLOG_INFO("productVersion[{}]", QSysInfo::productVersion().toUtf8().data()); + + setAttribute(Qt::WA_DeleteOnClose, true); + //setAttribute(Qt::WA_QuitOnClose, false); + + //QFont font("STXingkai", 40); // 设置默认值 + //m_textEdit->setFont(font); + m_edit->setTextColor(Qt::red); + m_edit->hide(); + + // 注意显示器摆放的位置不相同~;最大化的可能异常修复 +#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) + setWindowFlags(Qt::FramelessWindowHint | windowFlags()); // | Qt::WindowStaysOnTopHint + #ifdef _MYDEBUG + setWindowFlag(Qt::WindowStaysOnTopHint, false); // 删除置顶 + m_virGeom = currentScreen(QCursor::pos())->geometry(); + if (m_scrns.size() == 1) + m_virGeom.setWidth(m_virGeom.width() / 2); + #endif + + setFixedSize(m_virGeom.size()); +#else // Q_OS_MAC +// setWindowFlags(Qt::Window); // 不设置则 mac 下 devicePixelRatio: 1 + #ifdef _MYDEBUG + m_virGeom = currentScreen(QCursor::pos())->geometry(); + if (m_scrns.size() == 1) + m_virGeom.setWidth(m_virGeom.width() / 2); + setFixedSize(m_virGeom.size()); + #else + showFullScreen(); + #endif +#endif + + move(m_virGeom.topLeft()); + setMouseTracking(true); + m_rtCalcu.scrnType = ScrnType::Wait; + + // new refactor + m_selSize->setVisible(false); + m_selBar->setVisible(false); + m_paraBar->setVisible(false); + connect(m_selBar, &SelectBar::sigEnableDraw, this, &ScreenShot::onEnableDraw); + connect(m_selBar, &SelectBar::sigSelShape, this, &ScreenShot::onSelShape); + connect(m_selBar, &SelectBar::sigRevocation, this, &ScreenShot::onRevocation); + connect(m_selBar, &SelectBar::sigRenewal, this, &ScreenShot::onRenewal); + connect(m_selBar, &SelectBar::sigPin, this, &ScreenShot::onPin); + connect(m_selBar, &SelectBar::sigSave, this, &ScreenShot::onSave); + connect(m_selBar, &SelectBar::sigCancel, this, &ScreenShot::onCancel); + connect(m_selBar, &SelectBar::sigFinish, this, &ScreenShot::onFinish); + + connect(m_paraBar, &ParameterBar::sigParaBtnId, this, &ScreenShot::onParaBtnId); + connect(m_paraBar, &ParameterBar::sigSelColor, this, &ScreenShot::onSelColor); + + connect(m_selBar, &SelectBar::sigEnableDraw, m_paraBar, &ParameterBar::onEnableDraw); + connect(m_selBar, &SelectBar::sigSelShape, m_paraBar, &ParameterBar::onSelShape); + connect(this, &ScreenShot::sigClearScreen, this, &ScreenShot::onClearScreen); +} + +ScreenShot::~ScreenShot() +{ +} + +ScrnType ScreenShot::updateScrnType(const QPoint pos) +{ + CursorArea cursArea = m_rtCalcu.getCursorArea(pos); + + // 后面加上绘画和选择 + if (cursArea == CursorArea::Internal) { + return ScrnType::Move; + } else if (cursArea == CursorArea::External) { + return ScrnType::Wait; + } else if (cursArea == CursorArea::Border) { + return ScrnType::Stretch; + } else { + return ScrnType::Wait; // 避免警告,不会运行到此 + } +} + +void ScreenShot::updateCursorShape(const QPoint pos) +{ + if (m_rtCalcu.scrnType == ScrnType::Draw) + return; + + CursorArea cursArea = m_rtCalcu.getCursorArea(pos, true); + if (m_rtCalcu.scrnType == ScrnType::Move) { + } else if (m_rtCalcu.scrnType == ScrnType::Select) { + setCursor(Qt::CrossCursor); + } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { + } else if (m_rtCalcu.scrnType == ScrnType::Wait) { + if (!XHelper::instance().smartWindow()) + return; + + if (cursArea == CursorArea::External) { + + (m_rtSmartWindow.contains(pos, false) && !m_bFirstSel) || m_rtCalcu.getSelRect().contains(pos, true) ? + setCursor(Qt::ArrowCursor) : setCursor(Qt::ForbiddenCursor); + } else if (cursArea == CursorArea::Internal){ + setCursor(Qt::SizeAllCursor); + // 已修复:光标移动到绘画工具栏时,光标显示异常,在里面的事件过滤器中修复此出逻辑纰漏。光标进入 tbBar 后, + // 光标坐标也【不会开始改变】,至少在 ScreenShot。 + } else { + updateBorderCursorShape(cursArea); + } + } +} + +// 功能函数,仅考虑边框状态 +void ScreenShot::updateBorderCursorShape(const CursorArea & cursArea) +{ + if (cursArea == CursorArea::Top || cursArea == CursorArea::Bottom) + setCursor(Qt::SizeVerCursor); + else if (cursArea == CursorArea::Left || cursArea == CursorArea::Right) + setCursor(Qt::SizeHorCursor); + else if (cursArea == CursorArea::TopLeft || cursArea == CursorArea::BottomRight) + setCursor(Qt::SizeFDiagCursor); + else if (cursArea == CursorArea::TopRight || cursArea == CursorArea::BottomLeft) + setCursor(Qt::SizeBDiagCursor); +} + +// 清空截图内容(当关闭 Esc、或完成截图时) +void ScreenShot::onClearScreen() +{ +#ifdef Q_OS_WIN +#elif defined(Q_OS_MAC) + setWindowFlags(Qt::SubWindow); + showNormal(); +#elif defined(Q_OS_LINUX) +#endif + + m_bFirstPress = false; + m_specifyRts.clear(); + + //m_screens、m_primaryScreen 还保留 + delete m_currPixmap; + m_currPixmap = nullptr; + m_pCurrShape = nullptr; + + m_vDrawed.clear(); + m_vDrawUndo.clear(); + m_rtCalcu.clear(); + + m_bFirstSel = false; + m_selSize->setVisible(false); + m_selBar->setVisible(false); + m_paraBar->setVisible(false); + m_step.clear(); + + m_rtSmartWindow = QRect(); + if (m_rtCalcu.scrnType == ScrnType::Wait) // 状态重置 + setMouseTracking(true); +}; + +void ScreenShot::onLineEndsChange(LineEnds ends) +{ + m_step.lineEnds = ends; +} + +void ScreenShot::onLineDasheChange(Qt::PenStyle dashes) +{ + m_step.pen.setStyle(dashes); +} + +void ScreenShot::onEnableDraw(bool enable) +{ + if (enable) { + m_rtCalcu.scrnType = ScrnType::Draw; + setMouseTracking(false); // Fix: 鼠标移动中会被自动绘画矩形,副作用绘画状态的光标不完美了(选中框内外的光标被固定了),严格不算 bug,一种外观特效 + // qInfo()<<"--------------onDrawStart"<(); + } else if (bLine) { + //int idx = property(std::to_string((int)shape).c_str()).value(); + //int penWidth = 0; + + //if (idx == 0) + // penWidth = 1; + //else if (idx == 1) + // penWidth = 2; + //else if (idx == 2) + // penWidth = 4; + } else if (bPen) { + } else if (bArrows) { + m_step.bStyele = property(std::to_string((int)shape).c_str()).value(); + } else if (bText) { + } else if (bSeriNum) { + } else { + //XLOG_INFO + } + //qDebug() << "--------@onDrawShape:" << int(m_step.shape); +} + +void ScreenShot::onRevocation() +{ + if (m_vDrawed.count() <= 0) + return; + + m_vDrawUndo.push_back(*(m_vDrawed.end() - 1)); + m_vDrawed.pop_back(); + qDebug() << "---->m_vDrawRevoke:" << m_vDrawUndo.count() << " m_vDraw:" << m_vDrawed.count(); + update(); +} + +void ScreenShot::onRenewal() +{ + if (m_vDrawUndo.count() <= 0) + return; + + m_vDrawed.push_back(*(m_vDrawUndo.end() - 1)); + m_vDrawUndo.pop_back(); + qDebug() << "---->m_vDrawRevoke:" << m_vDrawUndo.count() << " m_vDraw:" << m_vDrawed.count(); + update(); +} + +void ScreenShot::onPin() +{ + if (m_savePixmap.isNull()) + return; + + auto pin = new PinWidget(m_savePixmap, m_savePixmap.rect(), nullptr); // 使用 nullptr,不会泄露 + pin->move(m_rtCalcu.getSelRect().topLeft()); + pin->show(); + + clearnAndClose(); +} + +void ScreenShot::onSave() +{ + if (drawToCurrPixmap()) { + if (XHelper::instance().autoCpoyClip()) + QApplication::clipboard()->setPixmap(m_savePixmap); + + const QString imageName = XHelper::instance().formatToName(); + QString fileter(tr("Image Files(*.png);;Image Files(*.jpg);;All Files(*.*)")); + QString fileNmae = QFileDialog::getSaveFileName(this, tr("Save Files"), imageName, fileter); + if (fileNmae.isEmpty()) + return; + + QTime startTime = QTime::currentTime(); + m_savePixmap.save(fileNmae, nullptr, XHelper::instance().imgQuailty()); // 绘画在 m_savePixmap 中,若在 m_savePixmap 会有 selRect 的左上角的点的偏移 + QTime stopTime = QTime::currentTime(); + int elapsed = startTime.msecsTo(stopTime); + qDebug() << "save m_savePixmap tim =" << elapsed << "ms" << m_savePixmap.size(); + //m_currPixmap->save("a2.png"); + + // auto save pixmap + const QString path = XHelper::instance().formatToName(XHelper::instance().path(toAutoSavePath).trimmed()); + if (!path.isEmpty()) { + m_savePixmap.save(path + QDir::separator() + imageName); + } + } + + clearnAndClose(); +} + +void ScreenShot::clearnAndClose() +{ + emit sigClearScreen(); + close(); +} + +void ScreenShot::onCancel() +{ + clearnAndClose(); +} + +void ScreenShot::onFinish() +{ + if (drawToCurrPixmap() && !m_savePixmap.isNull()) + QApplication::clipboard()->setPixmap(m_savePixmap); + clearnAndClose(); +} + +void ScreenShot::onParaBtnId(DrawShape shape, QToolButton* tb) +{ + if (!tb) + return; + + const int idx = tb->objectName().right(1).toInt(); + const bool bRect = shape == DrawShape::Rectangles; + const bool bEllipses = shape == DrawShape::Ellipses; + const bool bMosaics = shape == DrawShape::Mosaics; + const bool bLine = shape == DrawShape::LineWidth; + const bool bPen = shape == DrawShape::Pen; + const bool bArrows = shape == DrawShape::Arrows; + const bool bText = shape == DrawShape::Text; + const bool bSeriNum = shape == DrawShape::SerialNumber; + + if (bRect || bEllipses || bMosaics) { + setProperty(std::to_string((int)shape).c_str(), idx); + m_step.bStyele = idx; + + } else if (bLine) { + int penWidth = 0; + + if (idx == 0) + penWidth = 1; + else if (idx == 1) + penWidth = 4; + else if (idx == 2) + penWidth = 10; + + m_step.pen.setWidth(penWidth); + + } else if (bPen) { + } else if (bArrows) { + setProperty(std::to_string((int)shape).c_str(), idx); + m_step.bStyele = idx; + } else if (bText) { + } else if (bSeriNum) { + } else { + //XLOG_INFO + } +} + +void ScreenShot::onSelColor(QColor col) +{ + m_step.pen.setColor(col); +} + +// 获取虚拟屏幕截图 +QPixmap* ScreenShot::getVirScrnPixmap() +{ + if (!m_currPixmap) { + QDesktopWidget* desktop = qApp->desktop(); // 获取桌面的窗体对象 + const QRect geom = currentScreen(QCursor::pos())->geometry(); + +#if defined(Q_OS_MAC) + m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), geom.x(), geom.y(), geom.width(), geom.height())); +#else + #ifdef _MYDEBUG + m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), geom.x(), geom.y(), geom.width(), geom.height())); + #else + // 多屏的矩形取并集 + m_currPixmap = new QPixmap(m_priScrn->grabWindow(desktop->winId(), m_virGeom.x(), m_virGeom.y(), m_virGeom.width(), m_virGeom.height())); + #endif +#endif + } + + //m_currPixmap->save("123456.png"); + return m_currPixmap; +} + +bool ScreenShot::drawToCurrPixmap() +{ + if (!m_savePixmap || !m_currPixmap) + return false; + + QPainter pa; + pa.begin(m_currPixmap); + for (XDrawStep& it : m_vDrawed) + drawStep(pa, it); + pa.end(); + + QRect rtSel(m_rtCalcu.getSelRect()); + const QRect rtAppVirDesktop(QPoint(0, 0), m_virGeom.size()); + m_rtCalcu.limitBound(rtSel, rtAppVirDesktop); + if (rtSel.width() > 0 && rtSel.height() > 0) + m_savePixmap = m_currPixmap->copy(QRect(rtSel.topLeft() * getDevicePixelRatio(), rtSel.size() * getDevicePixelRatio())); // 注意独立屏幕缩放比(eg: macox = 2) + + return (!m_savePixmap.isNull() && m_currPixmap); +} + +// 获取已绘画形状的矩形,准备拖曳移动其 +bool ScreenShot::getDrawedShapeRect() +{ + return false; +} + +void ScreenShot::drawBorderMac(QPainter & pa, QRect rt, int num, bool isRound) +{ + if (num == 0) + return; + + pa.save(); + pa.setRenderHint(QPainter::Antialiasing, false); + pa.setBrush(Qt::NoBrush); + QPen penWhite(QColor(255, 255, 255, 1 * 255), 1); + penWhite.setStyle(Qt::CustomDashLine); + penWhite.setDashOffset(0); + penWhite.setDashPattern(QVector() << 4 * ScreenShot::getScale() << 4 * ScreenShot::getScale()); + penWhite.setCapStyle(Qt::FlatCap); + pa.setPen(penWhite); + pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.right(), rt.top())); + pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.left(), rt.bottom())); + pa.drawLine(QPoint(rt.left(), rt.bottom()), QPoint(rt.right(), rt.bottom())); + pa.drawLine(QPoint(rt.right(), rt.top()), QPoint(rt.right(), rt.bottom())); + + QPen penBlack(penWhite); + penBlack.setColor(QColor(0, 0, 0, 1 * 255)); + penBlack.setDashOffset(4); + pa.setPen(penBlack); + pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.right(), rt.top())); + pa.drawLine(QPoint(rt.left(), rt.top()), QPoint(rt.left(), rt.bottom())); + pa.drawLine(QPoint(rt.left(), rt.bottom()), QPoint(rt.right(), rt.bottom())); + pa.drawLine(QPoint(rt.right(), rt.top()), QPoint(rt.right(), rt.bottom())); + + int x1 = 0; + int y1 = 0; + int x2 = 0; + int y2 = 0; + rt.getCoords(&x1, &y1, &x2, &y2); + + QVector ver = { QPoint(x1, y1), + QPoint(x2, y1), + QPoint(x1, y2), + QPoint(x2, y2), + QPoint((x1 + x2) / 2.0, y1), + QPoint((x1 + x2) / 2.0, y2), + QPoint(x1, (y1 + y2) / 2.0), + QPoint(x2, (y1 + y2) / 2.0) }; + + pa.setPen(QPen(Qt::white, 1.5)); + pa.setBrush(QColor(146, 146, 146, 1 * 255)); + QPoint offsetPos(HAIF_R_BORDER_MARK * ScreenShot::getScale(), HAIF_R_BORDER_MARK * ScreenShot::getScale()); + pa.setRenderHint(QPainter::Antialiasing, true); + + for (int i = 0; i < num; ++i) + pa.drawEllipse(ver[i], offsetPos.x(), offsetPos.y()); + + pa.restore(); +} + +void ScreenShot::drawBorderPS(QPainter& pa, QRect rt, bool isRound) +{ + pa.save(); + pa.setRenderHint(QPainter::Antialiasing, true); + QPen pen; + pen.setWidth(XHelper::instance().borderWidth() * m_scal + SELECT_ASSIST_RECT_PEN_WIDTH); + pen.setColor(XHelper::instance().borderColor()); // + pa.setPen(pen); + pa.setBrush(Qt::NoBrush); + + int x1 = rt.left(); + int y1 = rt.top(); + int x2 = rt.right(); + int y2 = rt.bottom(); + + const int penWidth = pen.width(); + const int penAssWidth = SELECT_ASSIST_RECT_WIDTH * m_scal; + + // hor 且补齐交叉角落的空缺的那一块 + QLine l1(QPoint(x1 - penWidth / 2, y1), QPoint(x1 + penAssWidth, y1)); + QLine l2(QPoint(x1 - penWidth / 2, y2), QPoint(x1 + penAssWidth, y2)); + QLine l3(QPoint(x2 + penWidth / 2, y1), QPoint(x2 - penAssWidth, y1)); + QLine l4(QPoint(x2 + penWidth / 2, y2), QPoint(x2 - penAssWidth, y2)); + + // ver + QLine l5(QPoint(x1, y1), QPoint(x1, y1 + penAssWidth)); + QLine l6(QPoint(x1, y2), QPoint(x1, y2 - penAssWidth)); + QLine l7(QPoint(x2, y1), QPoint(x2, y1 + penAssWidth)); + QLine l8(QPoint(x2, y2), QPoint(x2, y2 - penAssWidth)); + + pa.drawLine(l1.translated(QPoint(0, -penWidth / 2))); + pa.drawLine(l2.translated(QPoint(0, penWidth / 2))); + pa.drawLine(l3.translated(QPoint(0, -penWidth / 2))); + pa.drawLine(l4.translated(QPoint(0, penWidth / 2))); + pa.drawLine(l5.translated(QPoint(-penWidth / 2, 0))); + pa.drawLine(l6.translated(QPoint(-penWidth / 2, 0))); + pa.drawLine(l7.translated(QPoint(penWidth / 2, 0))); + pa.drawLine(l8.translated(QPoint(penWidth / 2, 0))); + + pen.setWidth(XHelper::instance().borderWidth() * m_scal); + pa.setPen(pen); + pa.drawRect(rt); + pa.restore(); +} + +// 绘画当前类型的一个图案形状; isUseOwn 为 true 使用自带的画笔等;true 使用上一个环境的 +void ScreenShot::drawStep(QPainter& pa, XDrawStep& step, bool isUseEnvContext) +{ + if (DrawShape::NoDraw == step.shape + || (m_pCurrShape == &step && m_rtCalcu.scrnType == ScrnType::Move)) // 选中图形的处于移动状态,跳过这不绘画 + return; + + //pa.save(); + //pa.setRenderHint(QPainter::SmoothPixmapTransform, true); + //pa.setRenderHint(QPainter::HighQualityAntialiasing, true); + pa.setRenderHint(QPainter::Antialiasing, true); + + if (!isUseEnvContext) { + if (step.bStyele == 0){ + //step.brush.setColor(Qt::NoBrush); + step.brush.setStyle(Qt::NoBrush); + } else if (step.bStyele == 1) { + step.brush.setColor(step.pen.color()); + step.brush.setStyle(Qt::SolidPattern); + } + + pa.setPen(step.pen); + pa.setBrush(step.brush); + pa.setFont(step.font); + } + + switch (step.shape) { + case DrawShape::Rectangles: { + if (step.rt.isEmpty()) + break; + + pa.drawRect(step.rt); + break; + } + case DrawShape::Ellipses: { + if (step.rt.isEmpty()) + break; + + pa.drawEllipse(step.rt.center(), step.rt.width() / 2, step.rt.height() / 2); + break; + } + case DrawShape::Arrows: { + step.brush.setColor(step.pen.color()); // TODO 2022.01.16: 可以优化到上面去的 if 判断 + step.brush.setStyle(Qt::SolidPattern); + pa.setBrush(step.brush); + + if (step.bStyele == 0) { + pa.drawLine(step.p1, step.p2); + } else if (step.bStyele == 1 || step.bStyele == 2) { + pa.drawLine(XHelper::instance().GetShorterLine(step.p1, step.p2)); // 0, 1, 2 + QPainterPath arrowPath = XHelper::instance().GetArrowHead(step.p1, step.p2); + pa.fillPath(arrowPath, step.brush); + } + break; + } + case DrawShape::Pen: { + + pa.drawPolyline(step.custPath.data(), step.custPath.size()); + break; + } + case DrawShape::Text: { + // 记住:这是每一个 step 都会绘画的 + if (step.bTextComplete && !step.bDisplay && step.text.size()) { + QFontMetrics metrics(m_edit->font()); + + //QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); + //QPoint pos(step.editPos.x(), step.editPos.y() + metrics.ascent() + metrics.leading()); // 偏移得到视觉的“正确” + + int flags = Qt::TextWordWrap; // 自动换行 + QRect textBoundingRect = metrics.boundingRect(QRect(0, 0, m_virGeom.width(), 0), flags, step.text); + + //http://qtdebug.com/qtbook-paint-text + //https://doc.qt.io/qt-5/qpainter.html#drawText-5 + //const QFont font(pa.font()); + + qDebug() << step.font.pointSize(); + pa.setFont(step.font); + pa.drawText(QRect(step.editPos, textBoundingRect.size()), flags, step.text); + //pa.setFont(font); + } + break; + } + case DrawShape::Mosaics: { + if (!m_currPixmap || step.rt.isEmpty()) // 优化,删除就很明显 + return; + + QPixmap mosaicPixmap = m_currPixmap->copy(QRect(mapFromGlobal(step.rt.topLeft()) * getDevicePixelRatio(), step.rt.size() * getDevicePixelRatio())); + if (step.bStyele == 0) { + const QPixmap* pix = XHelper::instance().SetMosaicSmooth(&mosaicPixmap, step.mscPx); + pa.drawPixmap(step.rt, *pix); + } else if (step.bStyele == 1) { + const QImage img = XHelper::instance().SetMosaicPixlelated(&mosaicPixmap, step.mscPx); + pa.drawImage(step.rt, img); + } + + break; + } + default: + break; + } + + //pa.restore(); +} + +bool ScreenShot::isDrawShape(XDrawStep& step) +{ + if (step.p1 == step.p2) + return false; + + //if (step.shape == DrawShape::Rectangles) { + //} else if (step.shape == DrawShape::Ellipses) { + + //} else if (step.shape == DrawShape::Pen) { + + //} else if (step.shape == DrawShape::Arrows) { + + //} else if (step.shape == DrawShape::Mosaics) { + + //} else if (step.shape == DrawShape::Text || step.shape == DrawShape::NoDraw) { + // return false; // 特殊处理,或者不处理 + //} else { + // return true; // 避免警告 + //} + + return true; +} + +const QVector ScreenShot::drawBarPosition(Qt::Orientation orien /*= Qt:: Horizonta*/, ToolBarOffset offset /*= ToolBarOffset::TBO_Middle*/) +{ + QVector vec; + if (!m_selBar || !m_paraBar) + return vec; + + const int space = B_SPACEING; + int selBarHeight = m_selBar->height(); + int paraBarHeight = m_paraBar->height(); + + if (orien == Qt::Vertical) { + selBarHeight = m_selBar->width(); + paraBarHeight = m_paraBar->width(); + } + + const int sBarHeight = space + selBarHeight; + const int pBarHeight = space + paraBarHeight; + const QRect rtSel(m_rtCalcu.getSelRect()); + const int barMaxTop = rtSel.top() - sBarHeight; + const int barMaxBottom = rtSel.bottom() + sBarHeight; + const int barMaxLeft = rtSel.left() - sBarHeight; + const int barMaxRight = rtSel.right() + sBarHeight; + +// QDesktopWidget* desktop = QApplication::desktop(); // 获取桌面的窗体对象 +// QRect rtScrn = desktop->screen(desktop->screenNumber(rtSel.bottomRight() - QPoint(0, 1)))->geometry(); // geometry 则左上角坐标非 0,0; (0, 1) 为修正底部置底后, 返回为(错的)另一个显示器 + QScreen* curScrn = qGuiApp->screenAt(rtSel.bottomRight()); + if (!curScrn) + curScrn = qGuiApp->screenAt(rtSel.topRight()); + if (!curScrn) + curScrn = qGuiApp->screenAt(QCursor::pos()); + + QRect rtScrn = curScrn->geometry(); // 取代上面的那个,但有个 bug,光标在底部或者之外,返回 nullptr + QPoint p1; // selBar + QPoint p2; // paraBar + if (orien == Qt::Horizontal) { + p1 = QPoint(rtSel.center().x() - m_selBar->width() / 2, rtSel.bottom() + space); // 默认底部中间 + p2 = QPoint(p1.x(), p1.y() + space + selBarHeight); + + if (offset == ToolBarOffset::TBO_Left) + p1.setX(rtSel.left()); + else if (offset == ToolBarOffset::TBO_Right) + p1.setX(rtSel.right() - m_selBar->width()); + + int topLimit = qMax(m_virGeom.top(), rtScrn.top()); + int bottomLimit = qMin(m_virGeom.bottom(), rtScrn.bottom()); + + if (barMaxTop > topLimit) { // 选中框,上未触顶 + if (barMaxBottom > bottomLimit) { // 底部触顶 + p1.setY(rtSel.top() - sBarHeight); + p2.setY(p1.y() - pBarHeight); + } + + if (m_paraBar->isVisible()) { + if (barMaxBottom + pBarHeight > bottomLimit) { + + if (barMaxTop - pBarHeight < topLimit) { + p1.setY(rtSel.top() + space); + p2.setY(rtSel.top() + sBarHeight + space); + } else { + p1.setY(rtSel.top() - sBarHeight); + p2.setY(p1.y() - pBarHeight); + } + } else { + p1.setY(rtSel.bottom() + space); + } + } + + } else { + if (barMaxBottom > bottomLimit + || (m_paraBar->isVisible() && barMaxBottom + pBarHeight > bottomLimit)) { // 底部触顶 + p1.setY(rtSel.top() + space); + p2.setY(rtSel.top() + sBarHeight + space); + } + } + + } else { + + p1 = QPoint(rtSel.left() - (selBarHeight + space), rtSel.center().y() - m_selBar->height() / 2); // 默认左侧中间 + p2 = QPoint(p1.x() - space * 2 - selBarHeight, p1.y()); + if (offset == ToolBarOffset::TBO_Top) + p1.setY(rtSel.top()); + else if (offset == ToolBarOffset::TBO_Bottom) + p1.setY(rtSel.bottom() - m_selBar->height()); + + int leftLimit = qMax(m_virGeom.left(), rtScrn.left()); + int rightLimit = qMin(m_virGeom.right(), rtScrn.right()); + + if (barMaxLeft > leftLimit) { // 选中框左边未触顶 + //if (barMaxRight > rightLimit) // 右侧触顶 + // basePos.setY(rtSel.bottom() - barHeight); + } else { + p1.setX(rtSel.right() + space); + p2.setX(p1.x() + space + selBarHeight); + + if (barMaxRight > rightLimit) { // 右侧触顶 + p1.setX(rtSel.left() + space); + p2.setX(p1.x() + space + selBarHeight); + } + } + + p2.setY(p1.y()); + } + + vec << p1 << p2; + return vec; +} + +const QPoint ScreenShot::drawSelSizePosition(const QRect rt) +{ + QPoint ret; + if (!m_selSize) + return ret; + + //QString str = QString("rtSel(%1, %2, %3 * %4) m_savePixmap.rect:%5 * %6").arg(rt.left()).arg(rt.top()).arg(rt.width()).arg(rt.height()) + // .arg(m_savePixmap.width()).arg(m_savePixmap.height()); + + QString str = QString("%1 x %2").arg(rt.width()).arg(rt.height()); + + if (str.compare(m_selSize->text()) != 0) { + emit m_selSize->sigTextChanged(str); + m_selSize->setText(str); + } + + ret.setX(rt.left()); + ret.setY(rt.top() - m_selSize->height() - SS_SPACE_TO_SELECTRECT); + return ret; +} + +void ScreenShot::selectedShapeMove(QPainter& pa) +{ + QRect rtCurMove; + if (m_pCurrShape && (m_rtCalcu.scrnType == ScrnType::Wait || m_rtCalcu.scrnType == ScrnType::Move)) { + pa.save(); + + rtCurMove = m_pCurrShape->rt.translated(m_rtCalcu.pos2 - m_rtCalcu.pos1); + pa.setRenderHint(QPainter::Antialiasing, false); + pa.setBrush(Qt::NoBrush); + QPen penWhite(QColor(255, 255, 255, 1 * 255), 1); + penWhite.setStyle(Qt::CustomDashLine); + penWhite.setDashOffset(0); + penWhite.setDashPattern(QVector() << 4 * ScreenShot::getScale() << 4 * ScreenShot::getScale()); + penWhite.setCapStyle(Qt::FlatCap); + pa.setPen(penWhite); + + const int r = 3; + QPen penBlack(penWhite); + penBlack.setColor(QColor(0, 0, 0, 1 * 255)); + penBlack.setDashOffset(4); + + if (m_pCurrShape->shape == DrawShape::Rectangles) { + pa.setBrush(m_pCurrShape->brush); + pa.setPen(m_pCurrShape->pen); + pa.drawRect(rtCurMove); + + pa.setBrush(Qt::NoBrush); + pa.setPen(penWhite); + pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); + pa.setPen(penBlack); + pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); + } else if (m_pCurrShape->shape == DrawShape::Ellipses) { + pa.setBrush(m_pCurrShape->brush); + pa.setPen(m_pCurrShape->pen); + pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); + + pa.setBrush(Qt::NoBrush); + pa.setPen(penWhite); + pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); + pa.setPen(penBlack); + pa.drawRect(rtCurMove.adjusted(-r, -r, r, r)); + + //pa.setBrush(Qt::NoBrush); + //pa.setPen(penWhite); + + //// 生成可填充的轮廓 + //QPainterPathStroker stroker; + //stroker.setCapStyle(Qt::RoundCap); // 端点风格 + //stroker.setJoinStyle(Qt::RoundJoin); // 连接样式 + //stroker.setDashPattern(Qt::DashLine); // 虚线图案 + //stroker.setWidth(penWhite.width()); // 宽度 + + //// 生成一个新路径(可填充区域),表示原始路径 path 的轮廓 + //QPainterPath outlinePath = stroker.createStroke(path); + + + //pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); + //pa.setPen(penBlack); + //pa.drawEllipse(rtCurMove.center(), rtCurMove.width() / 2, rtCurMove.height() / 2); + } + + pa.restore(); + } +} +void ScreenShot::whichShape() +{ + bool bChecked = false; // 点击是否选中 + const int r = 4; + QPoint pos(QCursor::pos()); + for (auto it = m_vDrawed.rbegin(); it != m_vDrawed.rend(); ++it) { + const auto& rt = it->rt; + const QRect& rtOut = rt.adjusted(-r, -r, r, r); + const QRect& rtIn = rt.adjusted(r, r, -r, -r); + + QPainterPath path; + path.setFillRule(Qt::OddEvenFill); + if (it->shape == DrawShape::Rectangles) { + path.addEllipse(rtOut); + if (it->bStyele == 0) + path.addEllipse(rtIn); + } else if (it->shape == DrawShape::Ellipses) { + + path.addEllipse(rtOut.center(), rtOut.width() / 2, rtOut.height() / 2); + if (it->bStyele == 0) + path.addEllipse(rtIn.center(), rtIn.width() / 2, rtIn.height() / 2); + } else if (it->shape == DrawShape::Arrows) { + // TODO 2022.07.12: 倾斜的矩形 + 三角形; 旋转坐标轴后绘画? + } + + if (path.contains(pos)) { + m_pCurrShape = &(*it); + bChecked = true; + return; + } + } + + m_pCurrShape = nullptr; // 没有选中任何一个 +} + +void ScreenShot::savePixmap(bool quickSave /*= true*/, bool autoSave /*= true*/) +{ + if (quickSave) { + //auto path = XHelper::instance().path(toQuickSavePath); + } +} + +// 样式一: 浅蓝色 +void ScreenShot::drawBorderBlue(QPainter& pa, QRect rt, int num, bool isRound) +{ + if (num == 0) + return; + + pa.setPen(Qt::NoPen); + pa.setBrush(Qt::NoBrush); + + QIcon icon(":/resources/icons/boardPoint_8px.svg"); + QPixmap pixmap = icon.pixmap(QSize(HAIF_R_BORDER_MARK, HAIF_R_BORDER_MARK) * 4 * ScreenShot::getScale()); + pixmap.setDevicePixelRatio(getDevicePixelRatio()); + + QPoint offsetPos(HAIF_R_BORDER_MARK * 2 * ScreenShot::getScale(), HAIF_R_BORDER_MARK * 2 * ScreenShot::getScale()) ; + pa.drawPixmap(rt.topLeft() - offsetPos, pixmap); + pa.drawPixmap(rt.topRight() - offsetPos, pixmap); + pa.drawPixmap(rt.bottomLeft() - offsetPos, pixmap); + pa.drawPixmap(rt.bottomRight() - offsetPos, pixmap); + + if (num <= 8) { + int x1 = 0; + int y1 = 0; + int x2 = 0; + int y2 = 0; + + rt.getCoords(&x1, &y1, &x2, &y2); + pa.drawPixmap(QPoint((x1 + x2) / 2, y1) - offsetPos, pixmap); + pa.drawPixmap(QPoint((x1 + x2) / 2, y2) - offsetPos, pixmap); + pa.drawPixmap(QPoint(x1, (y1 + y2) / 2) - offsetPos, pixmap); + pa.drawPixmap(QPoint(x2, (y1 + y2) / 2) - offsetPos, pixmap); + } +} + +void ScreenShot::showAllDrawedShape(QPainter& pa) +{ + QPoint posText(0, 100); + const int space = 15; +// QRect m_rtCalcu_selRect(m_rtCalcu.getSelRect()); + + int i = 0; + pa.drawText(posText + QPoint(0, space * i), QString("m_vDrawed:%0").arg(m_vDrawed.count())); + + //for (const auto& it : m_vDrawed){ + // pa.drawText(posText + QPoint(0, space * i), QString("m_vDrawed[%0]:[%1]") + // .arg(i++).arg(it.shape)); + //} + + //pa.drawText(posText + QPoint(0, space * 1), QString("pos1: (%1, %2) pos2: (%3, %4)") + // .arg(m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos1.y()).arg(m_rtCalcu.pos2.x()).arg(m_rtCalcu.pos2.y())); + //pa.drawText(posText + QPoint(0, space * 2), QString("pos2 - pos1:(%1, %2)") + // .arg(m_rtCalcu.pos2.x() - m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos2.y() - m_rtCalcu.pos1.y())); + //pa.drawText(posText + QPoint(0, space * 3), QString("m_rtCalcu.getSelRect(): (%1, %2, %3 * %4)") + // .arg(m_rtCalcu_selRect.x()).arg(m_rtCalcu_selRect.y()).arg(m_rtCalcu_selRect.width()).arg(m_rtCalcu_selRect.height())); + //pa.drawText(posText + QPoint(0, space * 4), QString("rtSel: (%1, %2, %3 * %4)") + // .arg(rtSel.x()).arg(rtSel.y()).arg(rtSel.width()).arg(rtSel.height())); + //pa.drawText(posText + QPoint(0, space * 5), QString("pos(): (%1, %2)") + // .arg(pos().x()).arg(pos().y())); + //pa.drawText(posText + QPoint(0, space * 6), QString("m_rtAtuoMonitor: (%1, %2), rtAtuoMonitor: (%3, %4)") + // .arg(m_rtAtuoMonitor.x()).arg(m_rtAtuoMonitor.y()).arg(rtAtuoMonitor.x()).arg(rtAtuoMonitor.y())); + //pa.drawText(posText + QPoint(0, space * 7), QString("m_rtCalcu.bSmartMonitor: %1") + // .arg(m_rtCalcu.bSmartMonitor)); +} + +// 效果:绘画的顺序重要 +void ScreenShot::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + if (m_rtCalcu.scrnType == ScrnType::Draw) + setCursor(Qt::CrossCursor); + + // 原始图案 + QPainter pa(this); // 改为始终从 (0, 0) 开始绘画 + pa.setBrush(Qt::NoBrush); + pa.setPen(Qt::NoPen); + if (m_currPixmap) + pa.drawPixmap(QPoint(0, 0), *m_currPixmap); + + // 选中矩形图片 + QRect rtSel(m_rtCalcu.getSelRect()); // 移动选中矩形 + if (m_bSmartWin) + rtSel = m_rtSmartWindow; + + const auto & virGeom = QRect(mapFromGlobal(m_virGeom.topLeft()), m_virGeom.size()); // 修复为相对窗口的 + m_rtCalcu.limitBound(rtSel, virGeom); // 修复边界时图片被拉伸 + if (rtSel.width() > 0 && rtSel.height() > 0) { + m_savePixmap = m_currPixmap->copy(QRect(rtSel.topLeft() * getDevicePixelRatio(), rtSel.size() * getDevicePixelRatio())); // 注意独立屏幕缩放比(eg: macox = 2) + pa.drawPixmap(rtSel, m_savePixmap); + + // 放大镜实现 + //QSize tSize(100, 100); + //auto& mousePos = QCursor::pos(); + ////QRect rtMagnifying(QPoint(mousePos.x() - tSize.width() / 2, mousePos.y() - tSize.height() / 2), tSize); + //QRect rtPick(mousePos * getDevicePixelRatio(), tSize * getDevicePixelRatio()); + //pa.drawPixmap(mousePos + QPoint(100, 100), m_currPixmap->copy(rtPick).scaled(tSize * 4, Qt::KeepAspectRatio)); // 放大 4 倍 + + // m_savePixmap 和 m_currPixmap 的地址没有改变,但前者的 cacheKey 总在变化??? + } + + // 画家准备 + pa.setRenderHint(QPainter::Antialiasing, true); + const int penWidth = HAIF_INTERVAL; // 画笔宽度 + QPen pen(QColor("#01bdff")); + pen.setWidth(penWidth); + pa.setPen(pen); + pa.setOpacity(1); + pa.setBrush(Qt::transparent); + + // 绘画图案 + for (XDrawStep& it : m_vDrawed) + drawStep(pa, it); + + // 绘画当前步 + pen.setWidth(penWidth / 2); + pen.setColor(Qt::green); + pa.setPen(pen); + drawStep(pa, m_step, false); + + // drawWinInfo(pa); + selectedShapeMove(pa); + drawMaskLayer(virGeom, rtSel, pa); + drawBorder(rtSel, pa); + drawCrosshair(pa); + drawToolBar(); + pen.setColor(Qt::white); + pa.setPen(pen); + + showDebugInfo(pa, rtSel); +} + +void ScreenShot::showDebugInfo(QPainter& pa, QRect& rtSel) +{ +#ifdef _MYDEBUG + // 调试的实时数据 + QFont font;//(font()); + font.setPointSize(12); // 默认大小为 9 + pa.setFont(font); + const int space = font.pointSize() * 2.5; + + QPoint tTopLeft; + tTopLeft.setX(m_priScrn->geometry().x()); + tTopLeft.setY(m_priScrn->size().height()); + + QPoint tPosText(tTopLeft.x(), tTopLeft.y() - 5 * space); + QRect m_rtCalcu_selRect(m_rtCalcu.getSelRect()); + + pa.drawText(tPosText - QPoint(0, space * -1), QString("Wait(0) Select(1) Move(2) Draw(3) Stretch(4)")); + pa.drawText(tPosText - QPoint(0, space * 0), QString("m_rtCalcu.scrnType: %1") + .arg(int(m_rtCalcu.scrnType))); + pa.drawText(tPosText - QPoint(0, space * 1), QString("pos1: (%1, %2) pos2: (%3, %4)") + .arg(m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos1.y()).arg(m_rtCalcu.pos2.x()).arg(m_rtCalcu.pos2.y())); + pa.drawText(tPosText - QPoint(0, space * 2), QString("pos2 - pos1:(%1, %2)") + .arg(m_rtCalcu.pos2.x() - m_rtCalcu.pos1.x()).arg(m_rtCalcu.pos2.y() - m_rtCalcu.pos1.y())); + pa.drawText(tPosText - QPoint(0, space * 3), QString("m_rtCalcu.getSelRect(): (%1, %2, %3 * %4)") + .arg(m_rtCalcu_selRect.x()).arg(m_rtCalcu_selRect.y()).arg(m_rtCalcu_selRect.width()).arg(m_rtCalcu_selRect.height())); + pa.drawText(tPosText - QPoint(0, space * 4), QString("rtSel: (%1, %2, %3 * %4)") + .arg(rtSel.x()).arg(rtSel.y()).arg(rtSel.width()).arg(rtSel.height())); + pa.drawText(tPosText - QPoint(0, space * 5), QString("pos(): (%1, %2)") + .arg(pos().x()).arg(pos().y())); + pa.drawText(tPosText - QPoint(0, space * 6), QString("m_rtAtuoMonitor: (%1, %2, %3 * %4)") + .arg(m_rtSmartWindow.x()).arg(m_rtSmartWindow.y()).arg(m_rtSmartWindow.width()).arg(m_rtSmartWindow.height())); + pa.drawText(tPosText - QPoint(0, space * 7), QString("XHelper::instance().smartWindow(): %1") + .arg(XHelper::instance().smartWindow())); + pa.drawText(tPosText - QPoint(0, space * 8), QString("m_vDrawed:%1").arg(m_vDrawed.count())); + if (m_pCurrShape) { + QRect rtCurMove = m_pCurrShape->rt.translated(m_rtCalcu.pos2 - m_rtCalcu.pos1); + pa.drawText(tPosText - QPoint(0, space * 9), QString("rtMoveTest(%1, %2, %3 * %4)").arg(rtCurMove.x()).arg(rtCurMove.y()) + .arg(rtCurMove.width()).arg(rtCurMove.height())); + } + pa.drawText(tPosText - QPoint(0, space * 10), QString("m_step=>pos1(%1, %2) pos2(%3 * %4) editPos(%5, %6) rt(%7, %8, %9 * %10) text:%11") + .arg(m_step.p1.x()).arg(m_step.p1.y()).arg(m_step.p2.x()).arg(m_step.p2.y()) + .arg(m_step.editPos.x()).arg(m_step.editPos.y()) + .arg(m_step.rt.x()).arg(m_step.rt.y()).arg(m_step.rt.width()).arg(m_step.rt.height()) + .arg(m_step.text)); + + const int tSpace = 10; + const int barHeight = m_selBar->height(); + //const QRect rtSel(m_rtCalcu.getSelRect()); + QPoint topLeft(rtSel.right() - m_selBar->width(), rtSel.bottom() + tSpace); + const int barMaxTop = rtSel.top() - tSpace - barHeight; + const int barMaxBottom = rtSel.bottom() + tSpace + barHeight; + + QDesktopWidget* desktop = QApplication::desktop(); // 获取桌面的窗体对象 + //QRect rtScrn = desktop->screen(desktop->screenNumber(rtSel.bottomRight()))->geometry(); // geometry 则左上角坐标非 0,0 + QRect rtScrn = m_scrns.at(desktop->screenNumber(rtSel.bottomRight()))->geometry(); + // QRect rtScrn = currentScreen(rtSel.bottomRight())->geometry(); // 使用此替换有个 bug, 不在时候返回空 + int topLimit = qMax(m_virGeom.top(), rtScrn.top()); + int bottomLimit = qMin(m_virGeom.bottom(), rtScrn.bottom()); + + pa.drawText(tPosText - QPoint(0, space * 11), QString("barMaxTop:%1 barMaxBottom:%2 m_rtVirDesktop 左上右下(%3, %4, %5 * %6)") + .arg(barMaxTop).arg(barMaxBottom).arg(m_virGeom.left()).arg(m_virGeom.top()).arg(m_virGeom.right()).arg(m_virGeom.bottom())); + pa.drawText(tPosText - QPoint(0, space * 12), QString("rtScrn 左上右下(%1, %2, %3 * %4) topLimit:%5 bottomLimit:%6") + .arg(rtScrn.left()).arg(rtScrn.top()).arg(rtScrn.right()).arg(rtScrn.bottom()).arg(topLimit).arg(bottomLimit)); +#endif + +#if 0 + QRect rtOuter = m_rtCalcu.getExteRect(rtSel); + QRect rtInner = m_rtCalcu.getInteRect(rtSel); + int interval = (rtOuter.height() - rtInner.height()) / 2; + + QRect rtLeft(rtOuter.left(), rtInner.top(), interval, rtInner.height()); + QRect rtTop(rtInner.left(), rtOuter.top(), rtInner.width(), interval); + QRect rtRight(rtInner.right(), rtInner.top(), interval, rtInner.height()); + QRect rtBottom(rtInner.left(), rtInner.bottom(), rtInner.width(), interval); + QRect rtTopLeft(rtOuter.left(), rtOuter.top(), interval, interval); + QRect rtTopRight(rtInner.right(), rtOuter.top(), interval, interval); + QRect rtBottomLeft(rtOuter.left(), rtInner.bottom(), interval, interval); + QRect rtBottomRight(rtInner.right(), rtInner.bottom(), interval, interval); + + //pa.setBrush(Qt::blue); + + pa.setBrush(Qt::NoBrush); + pa.setPen(Qt::blue); + pa.drawRect(rtLeft); + pa.drawRect(rtTop); + pa.drawRect(rtRight); + pa.drawRect(rtBottom); + //pa.setBrush(Qt::yellow); + pa.setPen(Qt::white); + pa.drawRect(rtTopLeft); + pa.drawRect(rtTopRight); + pa.drawRect(rtBottomLeft); + pa.drawRect(rtBottomRight); + + /*qDebug() << "【paintEvent】 :" << m_rtCalcu.scrnType << m_rtCalcu.getSelRect() << rtSel << m_rtCalcu.getSelRect() << " " << m_rtCalcu.selEndPos << " " << m_basePixmap << " " << QRect();*/ + //<< "外部矩形:" << rtOuter << "内部矩形:" << rtInner; +#endif // 1 +} + +void ScreenShot::drawWinInfo(QPainter& pa) +{ + int i = 0; + pa.save(); + pa.setPen(QPen(Qt::black, 12)); + pa.setBrush(Qt::NoBrush); + for (const WinData& it : IWinInfo::m_vWinData) { + if (!it.bFilter) { + const QRect& rt = it.rect; + pa.drawRect(rt); + pa.drawText(QPoint(rt.topLeft()) + QPoint(0, 30), it.path); + pa.drawText(QPoint(rt.topLeft()) + QPoint(0, 10), it.title + QString("[%1]").arg(i++)); + } + } + + pa.restore(); +} + +void ScreenShot::drawMaskLayer(const QRect& virGeom, const QRect& rtSel, QPainter& pa) +{ + QPainterPath path; + path.addRect(virGeom); + path.addRect(rtSel); + path.setFillRule(Qt::OddEvenFill); + pa.save(); + pa.setPen(Qt::NoPen); + pa.setBrush(QColor(0, 0, 0, 0.5 * 255)); + pa.drawPath(path); + pa.restore(); +} + +void ScreenShot::drawBorder(QRect& rtSel, QPainter& pa) +{ + if (rtSel.width() < 0 || rtSel.height() < 0) + return; + pa.save(); + const QPen pen(QPen(XHelper::instance().borderColor(), XHelper::instance().borderWidth())); + pa.setPen(pen); + pa.setBrush(Qt::NoBrush); + m_selSize->move(drawSelSizePosition(rtSel)); + + QRect rt(rtSel); + for (auto& it = m_specifyRts.cbegin(); it != m_specifyRts.cend(); ++it) { + if (*it == rtSel) { + const int offset = pen.width(); + rt.adjust(offset, offset, -offset, -offset); + break; + } + } + + // 绘画边框样式 + const int styleIdx = XHelper::instance().boardStyle(); + if (styleIdx == 1) { + drawBorderPS(pa, rt); + } else if (styleIdx == 2) { + drawBorderMac(pa, rt); + } else if (styleIdx == 3) { + pa.drawRect(rt); + drawBorderBlue(pa, rt); + } + + pa.restore(); +} + +void ScreenShot::drawCrosshair(QPainter& pa) +{ + if (XHelper::instance().crosshair() && !m_bFirstPress) { + pa.save(); + pa.setPen(QPen(XHelper::instance().crosshairColor(), XHelper::instance().crosshairWidth())); + pa.setBrush(Qt::NoBrush); + + QPoint p(QCursor::pos()); + QLine l1(QPoint(m_virGeom.left(), p.y()), QPoint(m_virGeom.right(), p.y())); + QLine l2(QPoint(p.x(), m_virGeom.top()), QPoint(p.x(), m_virGeom.bottom())); + pa.drawLine(l1); + pa.drawLine(l2); + pa.restore(); + } +} + +void ScreenShot::drawToolBar() +{ + if (isVisible() && m_selBar && m_bFirstSel) { + const auto v = drawBarPosition(m_barOrien, ToolBarOffset::TBO_Middle); + + if (v.size() == 2) { + m_selBar->move(v.at(0)); + m_paraBar->move(v.at(1)); + } + + // 添加磨砂透明效果 + const double blurRadius = 7; + auto t1 = m_currPixmap->copy(QRect(v[0] * getDevicePixelRatio(), m_selBar->rect().size() * getDevicePixelRatio())); + m_selBar->setBlurBackground(t1, blurRadius); + auto t2 = m_currPixmap->copy(QRect(v[1] * getDevicePixelRatio(), m_paraBar->rect().size() * getDevicePixelRatio())); + m_paraBar->setBlurBackground(t2, blurRadius); + } +} + +void ScreenShot::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + qDebug() << "Key_Escape"; + emit sigClearScreen(); + // hide() 和 close() 区别: https://stackoverflow.com/questions/39407564 + //hide(); + close(); // // 销毁再不会有问题,由单例改写为 new 形式了。排查:1. tray 有关,改用 qpushbutton 和 close即可; 2.单例有关,该市建议修改为 new 指针的比较合适 + //deleteLater(); + } +} + +// 注意: 1. 按下、松开时候会切换状态;点击绘画按钮也会切换状态 +// 2. 开启鼠标跟踪时机;点击绘画按钮也会相应开启/关闭 +// 3. mousePressEvent、mouseMoveEvent、mouseReleaseEvent 合成整体来看;以及不忘记绘画按钮的槽函数 +void ScreenShot::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + m_bFirstPress = true; + setMouseTracking(false); + if (m_rtCalcu.getSelRect().isEmpty() && m_rtCalcu.scrnType == ScrnType::Wait) { + //m_rtCalcu.clear(); + + m_rtCalcu.scrnType = ScrnType::Select; + m_rtCalcu.pos1 = event->pos(); + m_rtCalcu.pos2 = m_rtCalcu.pos1; + } else if (m_rtCalcu.scrnType == ScrnType::Draw) { + m_step.p1 = event->pos(); + m_step.p2 = m_step.p1; + + if (m_step.shape == DrawShape::Text) { + QPoint perviousPos = m_step.editPos; // 修改之前的显示坐标 + + m_step.editPos = event->pos(); + m_step.rt = m_edit->rect(); + m_step.rt.setTopLeft(m_step.editPos); + + if (m_step.bDisplay) { // 输入框显示中 + //m_step.editPos = event->pos(); // pos1、pos2 松开鼠标时候会重置,而editPos不会 + //m_step.rt = m_textEdit->rect(); + //m_step.rt.setTopLeft(m_step.editPos); + //m_textEdit->move(m_step.editPos); + + if (!m_step.rt.contains(event->pos(), true)) { // 编辑完成 + m_step.bTextComplete = true; + m_step.bDisplay = false; + + m_step.text = m_edit->toPlainText(); + m_edit->clear(); + + m_step.editPos = perviousPos; + m_vDrawed.push_back(m_step); // 绘画文字为单独处理【暂时特例】 + m_step.idxLevel = m_step.totalIdx++; + } else { // 编辑ing + m_step.bTextComplete = false; + } + } else { // 输入框未显示 + m_step.bDisplay = true; + m_edit->clear(); + } + + m_edit->move(m_step.editPos); + m_edit->setVisible(m_step.bDisplay); + + XLOG_DEBUG("m_textEdit是否显示[{}] event->pos({}, {}) m_step.editPos({}, {}) perviousPos({}, {}) m_textEdit->rect({}, {}, {} * {}) m_textEdit->toPlainText[{}] m_step.text[{}]" + , m_edit->isVisible(), event->pos().x(), event->pos().y(), m_step.editPos.x(), m_step.editPos.y(), perviousPos.x(), perviousPos.y() + , m_edit->rect().left(), m_edit->rect().top(), m_edit->rect().width(), m_edit->rect().height() + , m_edit->toPlainText().toUtf8().data() + , m_step.text.toUtf8().data()); + } + + } else { // 则可能为移动、拉伸、等待状态 + m_rtCalcu.scrnType = updateScrnType(event->pos()); + } + + if (m_rtCalcu.scrnType == ScrnType::Move) { + m_rtCalcu.pos1 = event->pos(); + m_rtCalcu.pos2 = m_rtCalcu.pos1; + + whichShape(); // 判定移动选中的已绘图形 + } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { + m_rtCalcu.pos1 = event->pos(); + m_rtCalcu.pos2 = m_rtCalcu.pos1; + } + + update(); +} + +void ScreenShot::mouseMoveEvent(QMouseEvent *event) +{ +// if (event->button() != Qt::LeftButton) +// return; + + // 此时为 Qt::NoButton + if (m_rtCalcu.scrnType == ScrnType::Wait) { + if (m_bSmartWin) + updateGetWindowsInfo(); + } else if (m_rtCalcu.scrnType == ScrnType::Select) { + m_rtCalcu.pos2 = event->pos(); + m_bSmartWin = false; + //if (m_rtCalcu.pos1 != m_rtCalcu.pos2) + // 不显示 TODO: 2022.02.10 再添加一个变量即可 + } else if (m_rtCalcu.scrnType == ScrnType::Move) { + m_rtCalcu.pos2 = event->pos(); + } else if (m_rtCalcu.scrnType == ScrnType::Draw) { + m_step.p2 = event->pos(); + m_step.editPos = event->pos(); + + m_step.custPath.append(event->pos()); + m_step.rt = RectCalcu::getRect(m_step.p1, m_step.p2); + } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { + m_rtCalcu.pos2 = event->pos(); + } + + updateCursorShape(event->pos()); + update(); +} + +void ScreenShot::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) + return; + + if (m_rtCalcu.scrnType == ScrnType::Wait) { + } else if (m_rtCalcu.scrnType == ScrnType::Select) { + m_rtCalcu.pos2 = event->pos(); + + if (m_rtCalcu.pos1 == m_rtCalcu.pos2) { // 点击到一个点,视作智能检测窗口; 否则就是手动选择走下面逻辑 + m_rtCalcu.setRtSel(m_rtSmartWindow); + } + + m_bSmartWin = false; // 自动选择也结束 + + } else if (m_rtCalcu.scrnType == ScrnType::Move) { + m_rtCalcu.pos2 = event->pos(); + + // 移动选中的图形 + if (m_pCurrShape) { + m_pCurrShape->rt.translate(m_rtCalcu.pos2 - m_rtCalcu.pos1); + } + + } else if (m_rtCalcu.scrnType == ScrnType::Draw) { + m_step.p2 = event->pos(); + m_step.custPath.append(event->pos()); + m_step.rt = RectCalcu::getRect(m_step.p1, m_step.p2); + + // DrawShape::Text 在按下时候单独处理 m_vDrawed.push_back + if (m_step.shape != DrawShape::Text && m_step.shape != DrawShape::NoDraw) { + if (isDrawShape(m_step)) { + m_vDrawed.push_back(m_step); // TODO 2022.01.16 优化: 不必每次(无效得)点击,也都记录一次 + m_step.idxLevel = m_step.totalIdx++; + m_step.clear(); + } + } + + } else if (m_rtCalcu.scrnType == ScrnType::Stretch) { + m_rtCalcu.pos2 = event->pos(); + } + + if (!m_rtCalcu.calcurRsultOnce().isEmpty()) { // 计算一次结果 + m_bFirstSel = true; + if (!m_selBar->isVisible()) + m_selBar->setVisible(true); + + if (!m_selSize->isVisible()) + m_selSize->setVisible(true); + } + + if (m_rtCalcu.scrnType != ScrnType::Draw) { + m_rtCalcu.scrnType = ScrnType::Wait; + setMouseTracking(true); + } + + update(); +} + +void ScreenShot::wheelEvent(QWheelEvent* event) +{ + // Note: On X11 this value is driver specific and unreliable, use angleDelta() instead + // QPoint numPixels = event->pixelDelta(); + QPoint numDegrees = event->angleDelta() / 8; + QPoint numSteps = numDegrees / 15; + + if (numDegrees.isNull()) + return; + + if (m_step.shape == DrawShape::Text) { + // TODO 2022.07.03: 待优化,需同时调整输入框的大小 + m_step.font.setPointSize(m_step.font.pointSize() + numSteps.y()); + } else { + m_step.pen.setWidth(m_step.pen.width() + numSteps.y()); + if (m_step.pen.width() <= 0) + m_step.pen.setWidth(1); + if (m_step.pen.width() >= 100) + m_step.pen.setWidth(100); + } + + event->accept(); + update(); // TODO 2022.04.27: For debugging +} + +void ScreenShot::getScrnShots() +{ + m_specifyRts.clear(); + m_specifyRts.insert(m_virGeom); + for (const auto& it : m_scrns) + m_specifyRts.insert(it->geometry()); + +#ifdef Q_OS_WIN + //WinInfoWin::instance().getAllWinInfoCache(); + //if (m_rtCalcu.bSmartMonitor) // 存储所需要全部窗口信息 + //m_vec = WinInfoWin::instance().m_vWinInfo; +#elif defined(Q_OS_MAC) +#elif defined(Q_OS_LINUX) +#endif + + getScrnInfo(); + getVirScrnPixmap(); // 因 QWidget 启动后 事件执行顺序,sizeHint() -> showEvent() -> paintEvent();故全屏 show() 之前先获取桌面截图 + show(); + + if (m_bSmartWin) + updateGetWindowsInfo(); + + // fix: 初次使用全局热键召唤截图窗口,对 Esc 无响应。 考虑跨平台或需参考 https://zhuanlan.zhihu.com/p/161299504 + if (!isActiveWindow()) { + activateWindow(); + //setFocus(); + } +} + +// 屏幕详细参数 +void ScreenShot::getScrnInfo() +{ + XLOG_INFO("---------------QApplication::desktop() Info BEGIN----------------"); + XLOG_INFO("所有可用区域 m_virtualGeometry({}, {}, {} * {})", m_virGeom.left(), m_virGeom.top(), m_virGeom.width(), m_virGeom.height()); + XLOG_INFO("主屏可用区域 m_priScrn->geometry()({}, {}, {} * {})", m_priScrn->geometry().left(), m_priScrn->geometry().top(), m_priScrn->geometry().width(), m_priScrn->geometry().height()); + XLOG_INFO("是否开启虚拟桌面 isVirtualDesktop: {}", m_priScrn->virtualSiblings().size() > 1 ? true : false); // 等价于废弃的 QApplication::desktop()->isVirtualDesktop(); + XLOG_INFO("---------------QApplication::desktop() Info END----------------"); + + XLOG_INFO("---------------m_screens[] Info BEGIN----------------"); + for (const auto& it : m_scrns) { + XLOG_DEBUG("屏幕详细信息:index[{}]", m_scrns.indexOf(it)); + XLOG_DEBUG("size({}, {})", it->size().width(), it->size().height()); + XLOG_DEBUG("geometry({}, {}, {} * {})", it->geometry().left(), it->geometry().top(), it->geometry().width(), it->geometry().height()); + XLOG_DEBUG("availableGeometry({}, {}, {} * {})", it->availableGeometry().left(), it->availableGeometry().top(), it->availableGeometry().width(), it->availableGeometry().height()); + XLOG_DEBUG("scrn->virtualGeometry({}, {}, {} * {})", it->virtualGeometry().left(), it->virtualGeometry().top(), it->virtualGeometry().width(), it->virtualGeometry().height()); + + XLOG_INFO("设备像素比 devicePixelRatio[{}] 制造商 manufacturer[{}] 名称 name[{}]", it->devicePixelRatio(), it->manufacturer().toUtf8().data(), it->name().toUtf8().data()); + XLOG_INFO("序号 serialNumber[{}] 刷新率 refreshRate[{}] 模式 model[{}]", it->serialNumber().toUtf8().data(), it->refreshRate(), it->model().toUtf8().data()); + XLOG_INFO("虚拟几何 virtualGeometry:({}, {}, {} * {}) 缩放比 getScale[{}]", it->availableGeometry().left(), it->availableGeometry().top(), it->availableGeometry().width(), it->availableGeometry().height(), getScale(it)); + XLOG_INFO("物理几何 physicalSize:({} * {}) 大小 size({} * {})", it->physicalSize().width(), it->physicalSize().height(), it->physicalSize().width(), it->physicalSize().height()); + XLOG_INFO("物理 DPI physicalDotsPerInch: {} DPIX: {} DPIY: {} ", it->physicalDotsPerInch(), it->physicalDotsPerInchX(), it->physicalDotsPerInchY()); + XLOG_INFO("逻辑 DPI logicalDotsPerInch: {} DPIX: {} DPIY: {}\n", it->logicalDotsPerInch(), it->logicalDotsPerInchX(), it->logicalDotsPerInchX()); + } + + XLOG_INFO("m_virtualGeometry({}, {}, {} * {})\n", m_virGeom.left(), m_virGeom.top(), m_virGeom.width(), m_virGeom.height()); + XLOG_INFO("---------------m_screens[] Info END----------------"); +} + +double ScreenShot::getDevicePixelRatio() +{ +//#ifdef Q_OS_MAC +// return 2; // TODO 2022.03.11 无奈之举;使用 CMake MACOSX_BUNDLE 则返回的缩放比不正常, return 1 ??? +//#endif + return m_priScrn->devicePixelRatio(); +} + +double ScreenShot::getDevicePixelRatio(QScreen * screen) +{ + if (!screen) + return 1.0; + else + return screen->devicePixelRatio(); +} + +// 随着光标移动,更新获取桌面所有窗口信息 +void ScreenShot::updateGetWindowsInfo() +{ + WinID winId; + +#ifdef Q_OS_WIN + winId._hWnd = (void *)QWidget::winId(); + +#elif defined(Q_OS_MAC) +#elif defined(Q_OS_LINUX) + winId._xWindow = (unsigned long)0; +#endif + + Util::getRectFromCurrentPoint(winId, m_rtSmartWindow); + m_rtSmartWindow = QRect(mapFromGlobal(m_rtSmartWindow.topLeft()), m_rtSmartWindow.size()); +} + +double ScreenShot::getScale(QScreen * screen) +{ +#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) // or defined(Q_WS_WIN) || defined(Q_WS_X11) + double scale = screen->logicalDotsPerInch() / 96.0; + if (scale < 1.25) + return 1; + else if (1.25 <= scale && scale < 1.5) + return 1.25; + else if (1.5 <= scale && scale < 1.75) + return 1.5; + else if (1.75 <= scale && scale < 2) + return 1.75; + else if (2 <= scale && scale < 2.25) + return 2; + else if (2.25 <= scale && scale < 2.5) + return 2.25; + else if (2.5 <= scale && scale < 3) + return 2.5; + else if (3 <= scale && scale < 3.5) + return 3; + else if (3.5 <= scale && scale < 4) + return 3.5; + else + return scale; +#elif defined(Q_OS_MAC) + double scale = screen->logicalDotsPerInch() / 72.0; + return scale; +#else + return screen->logicalDotsPerInch() / 96.0; +#endif +} + +bool ScreenShot::isSelBorder() +{ + return !m_pCurrShape; +} + +const Qt::Orientation ScreenShot::getBarOrien() const +{ + return m_barOrien; +} + +void ScreenShot::setBarOrien(Qt::Orientation val) +{ + m_barOrien = val; +} + +const QScreen *ScreenShot::currentScreen(const QPoint& pos) +{ + const QScreen* curScrn = qGuiApp->screenAt(pos); + +#if defined(Q_OS_MACOS) + // On the MacOS if mouse position is at the edge of bottom or right sides + // qGuiApp->screenAt will return nullptr, so we need to try to find current + // screen by moving 1 pixel inside to the current desktop area + if (!curScrn && (pos.x() > 0 || pos.y() > 0)) + curScrn = qGuiApp->screenAt(QPoint(pos.x() - 1, pos.y() - 1)); +#endif + + //if (!curScrn) + // curScrn = qGuiApp->primaryScreen(); + + if (!curScrn) + qDebug() << "返回的屏幕为空??"; + + return curScrn; +} \ No newline at end of file diff --git a/src/screen/screenshot.h b/src/screen/screenshot.h index 01cbbc0..f176043 100644 --- a/src/screen/screenshot.h +++ b/src/screen/screenshot.h @@ -1,160 +1,154 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2021.09.29 - * Description: - ******************************************************************/ -#ifndef PICSHOT_WINFULLSCREEN_H -#define PICSHOT_WINFULLSCREEN_H - -#include "rectcalcu.h" -#include "drawhelper.h" -#include "../widget/xtextwidget.h" -#include "../tool/selectbar.h" -#include "../tool/parameterbar.h" -#include "../tool/selectsize/selectsize.h" -#include "../preference/appellation.h" -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN - #include "../platform/wininfo_win.h" -#elif defined(Q_OS_MAC) -#elif defined(Q_OS_LINUX) -#endif - -QT_BEGIN_NAMESPACE -//class QScreen; -class QPixmap; -QT_END_NAMESPACE - -// test -#include - -class ScreenShot : public QWidget -{ - Q_OBJECT -public: - ScreenShot(QWidget* parent = nullptr); - ~ScreenShot() override; - - void getScrnShots(); - static double getScale(QScreen *screen = QApplication::primaryScreen()); - bool isSelBorder(); - - const Qt::Orientation getBarOrien() const; - void setBarOrien(Qt::Orientation val); - -private: - const QScreen *currentScreen(const QPoint &pos = QCursor::pos()); - - void getScrnInfo(); - double getDevicePixelRatio(); - double getDevicePixelRatio(QScreen *screen); - - void updateGetWindowsInfo(); - void whichShape(); - - void savePixmap(bool quickSave = true, bool autoSave = true); - -signals: - void sigClearScreen(); - -public slots: - void onClearScreen(); - - // toolBar 的槽函数 - void onLineEndsChange(LineEnds ends); - void onLineDasheChange(Qt::PenStyle dashes); - - // new refactor - void onEnableDraw(bool enable); - void onSelShape(DrawShape shape, bool checked); - void onRevocation(); - void onRenewal(); - void onPin(); - void onSave(); - - void clearnAndClose(); - - void onCancel(); - void onFinish(); - - void onParaBtnId(DrawShape shape, QToolButton* tb); - void onSelColor(QColor col); - -private: - ScrnType updateScrnType(const QPoint pos); - void updateCursorShape(const QPoint pos); - void updateBorderCursorShape(const CursorArea& cursArea); - QPixmap* getVirScrnPixmap(); - bool drawToCurrPixmap(); - bool getDrawedShapeRect(); - - // 绘画边框样式 - void drawBorderBlue(QPainter& pa, QRect rt, int num = 8, bool isRound = true); // Blue 边框样式 - void drawBorderMac(QPainter& pa, QRect rt, int num = 8, bool isRound = true); // Mac 边框样式 - void drawBorderPS(QPainter& pa, QRect rt, bool isRound = false); // PicShot 边框样式 - - // 辅助(绘画)函数, TODO 可以迁移到 XDrawHelper 中? - void drawStep(QPainter& pa, XDrawStep &step, bool isUseEnvContext = false); - bool isDrawShape(XDrawStep& step); - - const QVector drawBarPosition(Qt::Orientation orien = Qt::Horizontal, ToolBarOffset offset = ToolBarOffset::TBO_Middle); - const QPoint drawSelSizePosition(const QRect rt); - - //--------------test begin------------- - void showAllDrawedShape(QPainter& pa); - //--------------test end------------- - - // refactor - void selectedShapeMove(QPainter& pa); // flameshot 选中图形的效果 - void drawCrosshair(QPainter& pa); - -protected: - void paintEvent(QPaintEvent *event) override; - void keyReleaseEvent(QKeyEvent* event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void wheelEvent(QWheelEvent* event) override; - -private: - double m_scal; - QList m_scrns; // 所有屏幕 - QScreen* m_priScrn; // 主屏幕 - QRect m_virGeom; // 截图时刻的虚拟桌面的大小 - QPixmap* m_currPixmap; // 当前屏幕截图 - QPixmap m_savePixmap; // 当前屏幕截图 + 遮罩 无构造初始化 - RectCalcu m_rtCalcu; // 选中矩形区域 - bool m_bSmartWin; // - bool m_bFirstSel; // 初次选中 截图矩形 完成 - bool m_bFirstPress; // 初次 左键按下 完成(十字线) - - XDrawStep m_step; // 当前绘画一步骤 - - QVector m_vDrawed; // 已绘步骤 - QVector m_vDrawUndo; // 撤销步骤 - QVector m_vWholeScrn; // 特殊矩形,全屏大小 - XDrawStep* m_pCurrShape; // 移动状态下的选中矩形; nullptr 为 最外层框, 非 nullptr 为具体选中 - - // test - XTextWidget* m_textEdit; - - // new refactor - QRect m_rtSmartWindow; // 自动检测窗口矩形大小;用以给其它赋值 - Qt::Orientation m_barOrien; - QPointer m_selSize; // 左上角显示窗口大小 - QPointer m_selBar; - QPointer m_paraBar; -}; - -#endif //PICSHOT_WINFULLSCREEN_H +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2021.09.29 + * Description: + ******************************************************************/ +#ifndef PICSHOT_WINFULLSCREEN_H +#define PICSHOT_WINFULLSCREEN_H + +#include "rectcalcu.h" +#include "drawhelper.h" +#include "../widget/xtextwidget.h" +#include "../tool/selectbar.h" +#include "../tool/parameterbar.h" +#include "../tool/selectsize/selectsize.h" +#include "../preference/appellation.h" +#include +#include +#include +#include +#include +#include +#include // QSet 是哈希表, std::set 是红黑树变种 + +struct comp { + bool operator()(const QRect& rt1, const QRect& rt2) const { return rt1.width() < rt2.width(); } +}; + +class QPixmap; +class ScreenShot : public QWidget +{ + Q_OBJECT +public: + ScreenShot(QWidget* parent = nullptr); + ~ScreenShot() override; + + void getScrnShots(); + static double getScale(QScreen *screen = QApplication::primaryScreen()); + bool isSelBorder(); + + const Qt::Orientation getBarOrien() const; + void setBarOrien(Qt::Orientation val); + +private: + const QScreen *currentScreen(const QPoint &pos = QCursor::pos()); + + void getScrnInfo(); + double getDevicePixelRatio(); + double getDevicePixelRatio(QScreen *screen); + + void updateGetWindowsInfo(); + void whichShape(); + + void savePixmap(bool quickSave = true, bool autoSave = true); + +signals: + void sigClearScreen(); + +public slots: + void onClearScreen(); + + // toolBar 的槽函数 + void onLineEndsChange(LineEnds ends); + void onLineDasheChange(Qt::PenStyle dashes); + + // new refactor + void onEnableDraw(bool enable); + void onSelShape(DrawShape shape, bool checked); + void onRevocation(); + void onRenewal(); + void onPin(); + void onSave(); + + void onCancel(); + void onFinish(); + void clearnAndClose(); + + void onParaBtnId(DrawShape shape, QToolButton* tb); + void onSelColor(QColor col); + +private: + ScrnType updateScrnType(const QPoint pos); + void updateCursorShape(const QPoint pos); + void updateBorderCursorShape(const CursorArea& cursArea); + QPixmap* getVirScrnPixmap(); + bool drawToCurrPixmap(); + bool getDrawedShapeRect(); + + void drawStep(QPainter& pa, XDrawStep &step, bool isUseEnvContext = false); // 辅助(绘画)函数, TODO 可以迁移到 XDrawHelper 中? + bool isDrawShape(XDrawStep& step); + + const QVector drawBarPosition(Qt::Orientation orien = Qt::Horizontal, ToolBarOffset offset = ToolBarOffset::TBO_Middle); + const QPoint drawSelSizePosition(const QRect rt); + + // Test + void showAllDrawedShape(QPainter& pa); + + // [paintEvent] refactor + void drawBorderBlue(QPainter& pa, QRect rt, int num = 8, bool isRound = true); // Blue 边框样式 + void drawBorderMac(QPainter& pa, QRect rt, int num = 8, bool isRound = true); // Mac 边框样式 + void drawBorderPS(QPainter& pa, QRect rt, bool isRound = false); // PicShot 边框样式 + + void drawWinInfo(QPainter& pa); // 绘画 窗口信息 path title 等 + void selectedShapeMove(QPainter& pa); // flameshot 选中图形的效果 + void drawMaskLayer(const QRect& virGeom, const QRect& rtSel, QPainter& pa); // 屏幕遮罩 + void drawBorder(QRect& rtSel, QPainter& pa); // 绘画边框 + void drawCrosshair(QPainter& pa); // 绘画十字线 + void drawToolBar(); // 绘画工具栏 + + void showDebugInfo(QPainter& pa, QRect& rtSel); // 显示实时的预览调试信息 + +protected: + void paintEvent(QPaintEvent *event) override; + void keyReleaseEvent(QKeyEvent* event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent* event) override; + +private: + double m_scal; + QList m_scrns; // 所有屏幕 + QScreen* m_priScrn; // 主屏幕 + QRect m_virGeom; // 截图时刻的虚拟桌面的大小 + QPixmap* m_currPixmap; // 当前屏幕截图 + QPixmap m_savePixmap; // 当前屏幕截图 + 遮罩 无构造初始化 + RectCalcu m_rtCalcu; // 选中矩形区域 + bool m_bSmartWin; // 是否开启智能窗口 + bool m_bFirstSel; // 初次选中 截图矩形 完成 + bool m_bFirstPress; // 初次 左键按下 完成(十字线) + + XDrawStep m_step; // 当前绘画一步骤 + + QVector m_vDrawed; // 已绘步骤 + QVector m_vDrawUndo; // 撤销步骤 + std::set m_specifyRts; // 特殊窗口的矩形,绘画时需略调整 + XDrawStep* m_pCurrShape; // 移动状态下的选中矩形; nullptr 为 最外层框, 非 nullptr 为具体选中 + + // test + XTextWidget* m_edit; + + // new refactor + QRect m_rtSmartWindow; // 自动检测窗口矩形大小;用以给其它赋值 + Qt::Orientation m_barOrien; + QPointer m_selSize; // 左上角显示窗口大小 + QPointer m_selBar; + QPointer m_paraBar; +}; + +#endif //PICSHOT_WINFULLSCREEN_H diff --git a/src/tool/base/colorparabar.h b/src/tool/base/colorparabar.h index b7f54c8..25203f4 100644 --- a/src/tool/base/colorparabar.h +++ b/src/tool/base/colorparabar.h @@ -1,62 +1,59 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2022.06.21 - * Description: 取色器面板 - ******************************************************************/ -#ifndef COLORPARABAR_H -#define COLORPARABAR_H - -#include -#include -#include -#include - -class XLabel; -class QLayout; -//class QGridLayout; - -//ColorParaBarMode -enum class ColorParaBarMode -{ - CPB_ParaBar, // 默认的 GridLayout 布局,用途为 draw bar 的取色器 - CPB_HighLight // QHBoxLayout 的水平布局,为活动色选择 -}; - -class ColorParaBar : public QWidget -{ - Q_OBJECT -public: - ColorParaBar(ColorParaBarMode mode = ColorParaBarMode::CPB_ParaBar, Qt::Orientations orien = Qt::Horizontal, QWidget *parent = nullptr); - virtual ~ColorParaBar() = default; - - void setOrientations(Qt::Orientations orien = Qt::Horizontal); - QColor setCurColor(const QColor col); - const QColor getCurColor(); - -signals: - void sigColorChange(const QColor&); - -private: - void onUpdateSel(const QColor& col); - -public: - bool eventFilter(QObject *watched, QEvent *event) override; - -protected: - void paintEvent(QPaintEvent *event) override; - -private: - double m_scal; - Qt::Orientations m_orien; - QLayout* m_layout; - QMap m_labMap; - XLabel* m_curXLab; - QColor m_curCol; -}; - -#endif // COLORPARABAR_H +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2022.06.21 + * Description: 取色器面板 + ******************************************************************/ +#ifndef COLORPARABAR_H +#define COLORPARABAR_H + +#include +#include +#include +#include + +enum class ColorParaBarMode +{ + CPB_ParaBar, // 默认的 GridLayout 布局,用途为 draw bar 的取色器 + CPB_HighLight // QHBoxLayout 的水平布局,为活动色选择 +}; + +class XLabel; +class QLayout; +class ColorParaBar : public QWidget +{ + Q_OBJECT +public: + ColorParaBar(ColorParaBarMode mode = ColorParaBarMode::CPB_ParaBar, Qt::Orientations orien = Qt::Horizontal, QWidget *parent = nullptr); + virtual ~ColorParaBar() = default; + + void setOrientations(Qt::Orientations orien = Qt::Horizontal); + QColor setCurColor(const QColor col); + const QColor getCurColor(); + +signals: + void sigColorChange(const QColor&); + +private: + void onUpdateSel(const QColor& col); + +public: + bool eventFilter(QObject *watched, QEvent *event) override; + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + double m_scal; + Qt::Orientations m_orien; + QLayout* m_layout; + QMap m_labMap; + XLabel* m_curXLab; + QColor m_curCol; +}; + +#endif // COLORPARABAR_H diff --git a/src/tool/base/managebar.h b/src/tool/base/managebar.h index 639aab3..ceb6cea 100644 --- a/src/tool/base/managebar.h +++ b/src/tool/base/managebar.h @@ -1,41 +1,38 @@ -/******************************************************************* - * Copyright (c) 2021-2022 偕臧 All rights reserved. - * - * Author: XMuli - * GitHub: https://github.com/XMuli - * Blog: https://xmuli.tech - * - * Date: 2022.06.26 - * Description: 参数工具栏的一部分,作为管理类:矩形、椭圆、箭头、马赛克等 - ******************************************************************/ -#ifndef MANAGEBAR_H -#define MANAGEBAR_H - -#include - -QT_BEGIN_NAMESPACE -class QBoxLayout; -QT_END_NAMESPACE - -class ManageBar : public QWidget -{ - Q_OBJECT -public: - explicit ManageBar(Qt::Orientations orien = Qt::Horizontal, QWidget* parent = nullptr); - virtual ~ManageBar() = default; - - void addWidget(QWidget* w); - void addSpacer(); - -private: - void initUI(); - -signals: - -private: - double m_scal; - Qt::Orientations m_orien; - QBoxLayout* m_layout; -}; - -#endif // MANAGEBAR_H +/******************************************************************* + * Copyright (c) 2021-2022 偕臧 All rights reserved. + * + * Author: XMuli + * GitHub: https://github.com/XMuli + * Blog: https://xmuli.tech + * + * Date: 2022.06.26 + * Description: 参数工具栏的一部分,作为管理类:矩形、椭圆、箭头、马赛克等 + ******************************************************************/ +#ifndef MANAGEBAR_H +#define MANAGEBAR_H + +#include + +class QBoxLayout; +class ManageBar : public QWidget +{ + Q_OBJECT +public: + explicit ManageBar(Qt::Orientations orien = Qt::Horizontal, QWidget* parent = nullptr); + virtual ~ManageBar() = default; + + void addWidget(QWidget* w); + void addSpacer(); + +private: + void initUI(); + +signals: + +private: + double m_scal; + Qt::Orientations m_orien; + QBoxLayout* m_layout; +}; + +#endif // MANAGEBAR_H diff --git a/src/xglobal.h b/src/xglobal.h index 09ebcfa..77d895b 100644 --- a/src/xglobal.h +++ b/src/xglobal.h @@ -12,7 +12,7 @@ #ifndef XGLOBAL_H #define XGLOBAL_H -// #define _MYDEBUG // 调试 + #define _MYDEBUG // 调试 // 1. HAIF_INTERVAL 为一半间隔,边框宽度一半高; 2 * HAIF_INTERVAL 为边框的宽度,为 getOuterRect - getRect == getRect - getInnerRect == HAIF_INTERVAL // 2. HAIF_INTERVAL 为边框一般的灵敏度,光标移动到上面便会变化形态