精易论坛

标题: 搓了个界面向导的轮子 [打印本页]

作者: 神女软件定制    时间: 2024-12-15 03:05
标题: 搓了个界面向导的轮子
本帖最后由 神女软件定制 于 2024-12-15 03:22 编辑



之前发布了个软件,结果界面有点杂乱,很多人不会用

搓了个界面向导的轮子,让用户方便理解一点
功能描述:
[C++] 纯文本查看 复制代码
CUITour tour;
//添加一个帧,有两个高亮区域
tour.AddFrame(_T("第1个页面的说明"), FALSE, CRect(0, 0, 100, 24), CRect(0, 32, 100, 56));
//添加一个帧,有3个高亮区域,分别绑定到指定的子控件句柄
tour.AddFrame(_T("第2个页面的说明\r\n第二行,巴拉巴拉"), FALSE, btn1.GetSafeHwnd(),btn2.GetSafeHwnd(),btn3.GetSafeHwnd());
//添加一个帧,没有高亮区域
tour.AddFrame(_T("The End"), FALSE);
tour.CreateTour(this->GetSafeHwnd());

可以在指定窗口上,创建一个向导控件,创建之前,添加好说明和对应的高亮区域或者控件(使用AddFrame)
当它被创建的时候,它会让主窗口变灰,依次显示每个帧的说明,和对应的高亮区域

高亮区域可以指定固定的矩形,也可以指定窗口句柄(对于控件可能会移动或调解尺寸的情况),可以混合指定0个或多个
得益于C++的可变参数模板,可以接收任意数量任意类型的参数,template <typename... Types> void AddFrame(CString tip, BOOL bUnion = FALSE, Types... args)


vc开源:
头文件:

[C++] 纯文本查看 复制代码
#pragma once
#include <afxwin.h>
#include <list>
#include <vector>
using namespace std;
class CUITour :
    public CWnd
{
public:
    BOOL CreateTour(HWND hwndTarget);
   
    //目标窗口,移动,改变尺寸,需要通知过来
    void TargetChange();
    template <typename... Types> void AddFrame(CString tip, BOOL bUnion = FALSE, Types... args) {
        FRAME frame;
        frame.tip = tip;
        frame.bUnion = bUnion;
        AddToList(frame.lsItems, args...);
        m_frames.push_back(frame);
    }
    void Step(int n);
    afx_msg void OnPaint();
    afx_msg void OnDestroy();
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
    afx_msg void OnKillFocus(CWnd* pNewWnd);
private:
    HWND m_hwndTarget;
    int m_cx;
    int m_cy;
    int m_nStep;
    CDC m_dcOrgin;
    CBitmap m_mapOrgin;
    CDC m_dcGray;
    CBitmap m_mapGray;
    CFont m_font;

    struct ITEM {
        enum {
            Rect, Handle,
        }type;
        CRect rc;
        HWND hwnd;
    };
    struct FRAME {
        CString tip;
        BOOL bUnion;
        vector<ITEM> lsItems;
    };
    vector<FRAME> m_frames;
    inline void AddToList(vector<ITEM>& ls) {

    }
    inline void AddToList(vector<ITEM>& ls, HWND hwnd) {
        ls.push_back({ ITEM::Handle,CRect(),hwnd });
    }
    inline void AddToList(vector<ITEM>& ls,CRect rc) {
        ls.push_back({ ITEM::Rect,rc,NULL });
    }
    template <typename T, typename... Types>void AddToList(vector<ITEM>& ls, T value, Types... args) {
        AddToList(ls, value);
        AddToList(ls, args...);
    }

    DECLARE_MESSAGE_MAP()
    };


源文件:
[C++] 纯文本查看 复制代码
#include "pch.h"
#include "CUITour.h"

BEGIN_MESSAGE_MAP(CUITour, CWnd)
        ON_WM_PAINT()
        ON_WM_DESTROY()
        ON_WM_LBUTTONDOWN()
        ON_WM_RBUTTONDOWN()
        ON_WM_KEYDOWN()
        ON_WM_MOUSEWHEEL()

        ON_WM_KILLFOCUS()
        
END_MESSAGE_MAP()

BOOL CUITour::CreateTour(HWND hwndTarget)
{
        if (!GetSafeHwnd()) {
                if (CWnd* pTarget = CWnd::FromHandle(hwndTarget)) {
                        CRect rc;
                        pTarget->GetClientRect(&rc);
                        pTarget->ClientToScreen(&rc);
                        static LPCTSTR clsName = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));
                        if (CreateEx(0, clsName, _T("UITour"), WS_VISIBLE | WS_POPUP, rc, pTarget, 0)) {
                                m_hwndTarget = hwndTarget;
                                m_nStep = 0;
                                CClientDC dcTemplate(this);
                                int cxScreen = GetSystemMetrics(SM_CXSCREEN);
                                int cyScreen = GetSystemMetrics(SM_CYSCREEN);
                                m_dcOrgin.CreateCompatibleDC(&dcTemplate);
                                m_mapOrgin.CreateCompatibleBitmap(&dcTemplate, cxScreen, cyScreen);
                                m_dcOrgin.SelectObject(m_mapOrgin);

                                m_dcGray.CreateCompatibleDC(&dcTemplate);
                                m_mapGray.CreateCompatibleBitmap(&dcTemplate, cxScreen, cyScreen);
                                m_dcGray.SelectObject(m_mapGray);

                                m_font.CreatePointFont(120, _T("SimHei"));
                                TargetChange();
                                ShowWindow(SW_SHOW);
                                return TRUE;
                        }
                }
        }
        return FALSE;
}

void CUITour::TargetChange()
{
        if (GetSafeHwnd()) {
                if (CWnd* pTarget = CWnd::FromHandle(m_hwndTarget)) {
                        CRect rc;
                        pTarget->GetClientRect(&rc);
                        pTarget->ClientToScreen(&rc);


                        m_cx = rc.Width();
                        m_cy = rc.Height();
                        pTarget->PrintWindow(&m_dcOrgin, PW_CLIENTONLY);
                        m_dcGray.PatBlt(0, 0, m_cx, m_cy, BLACKNESS);
                        BLENDFUNCTION blf{ 0 };
                        blf.BlendOp = AC_SRC_OVER;
                        blf.SourceConstantAlpha = 38;//36
                        m_dcGray.AlphaBlend(0, 0, m_cx, m_cy,
                                &m_dcOrgin, 0, 0, m_cx, m_cy, blf);
                        
                        
                        MoveWindow(rc);
                        InvalidateRect(NULL);//有可能并没有实际移动
                }
                else {//目标丢失
                        DestroyWindow();
                }
        }
}

void CUITour::Step(int nAdd)
{
        m_nStep += nAdd;
        if (m_nStep >= 0 && m_nStep < m_frames.size()) {
                InvalidateRect(NULL);
        }
        else {
                DestroyWindow();
        }
}


void CUITour::OnPaint()
{
        CPaintDC dc(this); // device context for painting
        // TODO: 在此处添加消息处理程序代码
        // 不为绘图消息调用 CWnd::OnPaint()
        dc.BitBlt(0, 0, m_cx, m_cy, &m_dcGray, 0, 0, SRCCOPY);
        CWnd* pTarget = CWnd::FromHandle(m_hwndTarget);
        if (m_nStep < m_frames.size()) {
                vector<CRect> rcItems;
                for (auto& e : m_frames[m_nStep].lsItems) {//采集所有矩形
                        if (e.type == ITEM::Handle) {
                                e.rc.SetRectEmpty();
                                if (CWnd* pWnd = CWnd::FromHandle(e.hwnd)) {
                                        pWnd->GetWindowRect(e.rc);
                                        ScreenToClient(e.rc);
                                }
                        }
                        rcItems.push_back(e.rc);
                }
                if (m_frames[m_nStep].bUnion) {//判断是不是联合
                        CRect rcUnion;
                        for (auto& e : rcItems) {
                                rcUnion.UnionRect(rcUnion, e);
                        }
                        rcItems.resize(1);
                        rcItems[0] = rcUnion;
                }
               

                for (auto& e : rcItems) {
                        dc.BitBlt(e.left, e.top, e.Width(), e.Height(), &m_dcOrgin, e.left, e.top, SRCCOPY);
                }
               
                list<CRect> rcLeft = { CRect(0,0,m_cx,m_cy) };
                for (auto& e : rcItems) {//分裂矩形,求出所有剩下的小矩形区域
                        for (auto& itor = rcLeft.begin(); itor != rcLeft.end();) {
                                CRect rcTemp;
                                if (rcTemp.IntersectRect(*itor, e)) {//有交集
                                        //itor表示大矩形

                                        if (itor->left < e.left) {//左
                                                rcLeft.push_back(CRect(itor->left, itor->top, e.left, itor->bottom));
                                        }
                                        if (itor->right > e.right) {//右
                                                rcLeft.push_back(CRect(e.right, itor->top, itor->right, itor->bottom));
                                        }

                                        if (itor->top < e.top) {//上
                                                rcLeft.push_back(CRect(itor->left, itor->top, itor->right, e.top));
                                        }
                                        if (itor->bottom > e.bottom) {//下
                                                rcLeft.push_back(CRect(itor->left, e.bottom, itor->right, itor->bottom));
                                        }

                                        rcLeft.erase(itor++);
                                        //break;
                                }
                                else {
                                        itor++;
                                }
                        }
                }
#if false
                TCHAR buff[128];
                wsprintf(buff, _T("空白区域数量: %d \r\n"), rcLeft.size());
                OutputDebugString(buff);
               
                for (auto& rc : rcLeft) {
                        CPen pen;
                        pen.CreatePen(PS_DASH, 1, rand());
                        dc.SelectObject(pen);
                        dc.SelectObject(::GetStockObject(NULL_BRUSH));
                        dc.Rectangle(rc);
                        dc.MoveTo(rc.left, rc.top);
                        dc.LineTo(rc.right, rc.bottom);
                        dc.MoveTo(rc.right, rc.top);
                        dc.LineTo(rc.left, rc.bottom);
                }
#endif
                CString strTip;
                strTip.Format(_T("%s\r\n\r\n< UI向导 %d/%lld >"), m_frames[m_nStep].tip, m_nStep + 1, m_frames.size());
                dc.SetBkMode(TRANSPARENT);
                dc.SetTextColor(RGB(255, 255, 255));
                dc.SelectObject(m_font);
                auto &itor=max_element(rcLeft.begin(), rcLeft.end(), [](const CRect& a, const CRect& b) {
                        return b.Width() * b.Height() > a.Width() * a.Height();
                        if (min(b.Width(), b.Height()) > min(a.Width(), a.Height())) {
                                return true;
                        }
                        else if (min(b.Width(), b.Height()) == min(a.Width(), a.Height())) {
                                if (max(b.Width(), b.Height()) > max(a.Width(), a.Height())) {
                                        return true;
                                }
                        }
                        return false;
                        });
                CRect rcOrigon = *itor;
                CRect rc= rcOrigon;
                UINT nFormat = DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS;
                dc.DrawText(strTip, &rc, nFormat | DT_CALCRECT);
                if (rcOrigon.Height() > rc.Height()) {
                        rcOrigon.top += (rcOrigon.Height() - rc.Height()) / 2;
                }
               
                dc.DrawText(strTip, &rcOrigon, nFormat);
        }
}

void CUITour::OnDestroy()
{
        CWnd::OnDestroy();

        // TODO: 在此处添加消息处理程序代码
        m_dcOrgin.DeleteDC();
        m_mapOrgin.DeleteObject();
        m_dcGray.DeleteDC();
        m_mapGray.DeleteObject();
        m_font.DeleteObject();
}

void CUITour::OnLButtonDown(UINT nFlags, CPoint point)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(1);
        CWnd::OnLButtonDown(nFlags, point);
}


void CUITour::OnRButtonDown(UINT nFlags, CPoint point)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(-1);
        CWnd::OnRButtonDown(nFlags, point);
}



void CUITour::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        int nAdd = 0;
        switch (nChar)
        {
        case VK_ESCAPE:
                DestroyWindow();
                return;
        case VK_LEFT:
        case VK_UP:
        case VK_PRIOR:
                nAdd = -1;
                break;
        case VK_RIGHT:
        case VK_DOWN:
        case VK_NEXT:
        case VK_SPACE:
        case VK_RETURN:
                nAdd = 1;
                break;
        case VK_HOME:
                nAdd = -m_nStep;
                break;
        case VK_END:
                nAdd = m_frames.size() - m_nStep - 1;
                break;
        default:
                break;
        }
        if(nAdd)
                Step(nAdd);
        CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

BOOL CUITour::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
        // TODO: 在此添加消息处理程序代码和/或调用默认值
        Step(zDelta < 0 ? 1 : -1);
        return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

void CUITour::OnKillFocus(CWnd* pNewWnd)
{
        CWnd::OnKillFocus(pNewWnd);

        // TODO: 在此处添加消息处理程序代码
        //SetFocus();
#if false
        if (pNewWnd->GetSafeHwnd() == m_hwndTarget) {
                OutputDebugString(_T("主窗口得到焦点\r\n"));
        }
        else if (pNewWnd->GetSafeHwnd() == GetSafeHwnd()) {
                OutputDebugString(_T("自己得到焦点\r\n"));
        }
        else {
                TCHAR buff[128];
                wsprintf(buff, _T("焦点: %d \r\n"), pNewWnd->GetSafeHwnd());
                OutputDebugString(buff);
        }
#endif
}


有空了再转成易语言的



作者: 憨憨问号    时间: 2024-12-15 16:43
有意思啊,快快转!这个好有用!




欢迎光临 精易论坛 (https://125.confly.eu.org/) Powered by Discuz! X3.4