QuickUp v4 新功能一览

QuickUp v4 新功能一览

引言

项目地址:Smart-Space/QuickUp: 一个Windows快捷应用组启动器QuickUp: QuickUp Gitee

视频简介:QuickUp 4.0 前瞻

以下为文本介绍,具体实现见源码仓库。


尝试调整任务窗口排布

通过监听窗口创建、进程任务名匹配方式尽可能获得准确的窗口句柄:

cpp 复制代码
namespace WindowMonitor {
    // 带有时间戳的窗口记录
    struct WindowRecord {
        HWND hwnd;
        std::chrono::steady_clock::time_point showTime;

        bool operator==(const HWND& other) const {
            return hwnd == other;
        }
    };

    std::vector<WindowRecord> recentWindows;
    std::mutex rwMutex;
    
    std::thread monitorThread;
    std::atomic<bool> isListening{false};
    DWORD threadId = 0;
    HWINEVENTHOOK hEventHook = nullptr;

    const size_t MAX_RECENT_WINDOWS = 30;

    // WinEvent 回调函数
    void CALLBACK WinEventProc(
        HWINEVENTHOOK hWinEventHook,
        DWORD event,
        HWND hwnd,
        LONG idObject,
        LONG idChild,
        DWORD dwEventThread,
        DWORD dwmsEventTime
    ) {
        // 只关心顶层窗口本身的事件
        if (idObject != OBJID_WINDOW || idChild != CHILDID_SELF || hwnd == nullptr) {
            return;
        }

        // 过滤掉非顶层窗口
        if (GetParent(hwnd) != NULL) {
            return;
        }

        // 过滤掉明显的非主窗口 (例如没有所有者的弹出窗口)
        if (GetWindow(hwnd, GW_OWNER) != NULL) {
            return;
        }

        // 跳过不含标题栏的某些阴影或辅助窗口
        long style = GetWindowLong(hwnd, GWL_STYLE);
        if (!(style & WS_CAPTION)) {
            return;
        }

        auto now = std::chrono::steady_clock::now();

        std::lock_guard<std::mutex> lock(rwMutex);
        
        // 避免重复添加相同的 HWND
        std::erase_if(recentWindows, [hwnd](const WindowRecord& r) {
            return r.hwnd == hwnd;
        });
        
        // 最新的在最后
        recentWindows.push_back({hwnd, now});

        // 维持 vector 大小
        if (recentWindows.size() > MAX_RECENT_WINDOWS) {
            recentWindows.erase(recentWindows.begin());
        }
    }

    // 线程执行的监听循环
    void MessageLoop() {
        // 记录当前线程ID
        threadId = GetCurrentThreadId();

        hEventHook = SetWinEventHook(
            EVENT_OBJECT_SHOW, EVENT_OBJECT_SHOW,
            NULL,
            WinEventProc,
            0, 0, // 监听所有进程和线程
            WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS // 进程外回调,跳过自己
        );

        if (!hEventHook) {
            // std::wcerr << L"Failed to set WinEventHook!" << std::endl;
            return;
        }

        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        // 退出前清理
        if (hEventHook) {
            UnhookWinEvent(hEventHook);
            hEventHook = nullptr;
        }
    }

    //==========外部的接口==========

    void Start() {
        if (isListening) return;
        isListening = true;
        
        {
            std::lock_guard<std::mutex> lock(rwMutex);
            recentWindows.clear();
            recentWindows.reserve(MAX_RECENT_WINDOWS+1);// 预留一个位置
        }

        monitorThread = std::thread(MessageLoop);
    }

    void Stop() {
        if (!isListening) return;
        isListening = false;

        // 向子线程发送 WM_QUIT 消息
        if (threadId != 0) {
            PostThreadMessage(threadId, WM_QUIT, 0, 0);
        }

        if (monitorThread.joinable()) {
            monitorThread.join();
        }

        {
            std::lock_guard<std::mutex> lock(rwMutex);
            recentWindows.clear();
        }
        threadId = 0;
    }

    // 获取最近窗口的一份拷贝,以便主线程慢慢处理而不长时间占用锁
    std::vector<WindowRecord> GetRecentWindowsReversed() {
        std::lock_guard<std::mutex> lock(rwMutex);
        std::vector<WindowRecord> copy = recentWindows;
        std::reverse(copy.begin(), copy.end()); // 翻转,最新的在最前面
        return copy;
    }
}

以上非完整代码,但已经足够展现框架和逻辑。

接下来通过SetWindowPos调整窗口位置和尺寸。需要注意,SetWindowPos实际上调整的是整体窗口,而非窗口可见区域。在Windows上,窗口可能包含渲染阴影、不可见边框等等,因此需要转换(该代码见源码zoneutils.h中的WindowMonitor::AdjustRectForSizeWindowToRect,极大参考了powertoys)。窗口移动代码如下:

cpp 复制代码
void SmoothMoveWindow(HWND hwnd, int x, int y, int w, int h, bool zone_round) {
    if (IsIconic(hwnd)) {
        ShowWindow(hwnd, SW_RESTORE);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
	
    // 是否保留圆角
    if (!zone_round) {
        WindowMonitor::DisableRoundCorners(hwnd);
    }

    RECT rect = { x, y, x + w, y + h };
    rect = WindowMonitor::AdjustRectForSizeWindowToRect(hwnd, rect);
    x = rect.left;
    y = rect.top;
    w = rect.right - x;
    h = rect.bottom - y;

    UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS;
    SetWindowPos(hwnd, NULL, x, y, w, h, flags);
}

显示任务进展

这是个UI提示元素,这是防止在任务运行期间多次点击运行、修改或删除,但这些仍然可以通过快捷键进行操作(运行和修改,删除不支持快捷键),顺便进行提示。

正式支持隐藏任务

在之前,QuickUp会自动略过以[x]结尾的任务。在4.0版本中,QuickUp正式启用了对隐藏任务的全流程支持:

  • 隐藏任务、普通任务的切换
  • 任务名、任务可见性双向感知
  • 支持显示隐藏任务

隐藏任务非常有用,当一个任务被多个其他任务调用,而自身并不常用;或者已经作为快捷方式被放到桌面、开始菜单或者其他什么地方时,就显得非常方便实用。

以第二个情景为例(创造需求中......😵)。众所周知,Windows下,git默认不走系统代理,如果代理应用只用系统代理且也不是一直使用的话,就需要:

  • 打开代理
  • 设置git proxy
  • 关闭代理
  • 还原proxy以正常使用

那在QuickUp就这样设置:

创建任务快捷方式、(可以)改名快捷方式并(随意)放上自己喜欢的图标、留在桌面或者移动到开始菜单文件夹以替换代理应用快捷方式、隐藏原任务。这样,点击快捷方式后,先设置proxy、再启用代理应用,等到代理应用关闭后,再还原proxy。同时,也不会QuickUp界面大眼瞪小眼占位置。

相关推荐
源码之家12 小时前
计算机毕业设计:Python二手车交易价格预测分析平台 Django框架 随机森林 可视化 数据分析 汽车 车辆 大数据 hadoop(建议收藏)✅
大数据·爬虫·python·机器学习·django·汽车·课程设计
清水白石00813 小时前
Python 性能优化避坑指南:回归风险防控、基准压测与安全回滚实战
python·性能优化·回归
好家伙VCC13 小时前
# 发散创新:基于状态通道的以太坊智能合约高效交互实践在区块链应用开发中,**交易
java·python·区块链·智能合约
gc_229913 小时前
学习python使用Ultralytics的YOLO26进行分类的基本用法
python·分类·ultralytics·yolo26
书到用时方恨少!13 小时前
Python 零基础入门系列(终篇):综合实战项目
开发语言·python
小陈工13 小时前
Python Web开发入门(二):Flask vs Django,项目结构大比拼
前端·数据库·python·安全·web安全·django·flask
杜子不疼.13 小时前
2026 GitHub 热门 Python 项目:AI 代理与数据工具精选
人工智能·python·github
迷藏49413 小时前
# 发散创新:用Rust构建高性能分布式账本节点——从零实现共识算法与链上数据存储
java·python·rust·共识算法·分布式账本
进击的小头13 小时前
第21篇:BUCK变换器双环控制系统设计与参数整定调试实战
python·算法
智算菩萨13 小时前
【Tkinter】3 Tkinter Button 控件深度解析:从事件绑定到现代交互设计实战
python·microsoft·ui·交互·tkinter·button