mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-02 19:26:32 +00:00
[MSPAINT] Update cursor shape for brush and rubber (#8974)
Improve UI/UX by updating mouse cursor shape. JIRA issue: CORE-20520 - Add CStyledCursor class. - Enhance CCanvasWindow::OnSetCursor for TOOL_BRUSH and TOOL_RUBBER. - Add CCanvasWindow::m_hBrushCursor CCanvasWindow::m_hRubberCursor to keep cursor shapes. - Send WM_SETCURSOR from ToolsModel to update the cursor shape. Co-authored-by: Hermès BÉLUSCA - MAÏTO <[email protected]>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
|
||||
* PURPOSE: Providing the canvas window class
|
||||
* COPYRIGHT: Copyright 2015 Benedikt Freisen <[email protected]>
|
||||
* Copyright 2026 Katayama Hirofumi MZ <[email protected]>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
@@ -11,6 +12,137 @@ CCanvasWindow canvasWindow;
|
||||
|
||||
/* FUNCTIONS ********************************************************/
|
||||
|
||||
HCURSOR
|
||||
CStyledCursor::CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber)
|
||||
{
|
||||
const INT diameter = 2 * radius;
|
||||
if (diameter <= 2)
|
||||
{
|
||||
HCURSOR hCursor = ::LoadCursor(NULL, IDC_CROSS);
|
||||
return hCursor ? CopyCursor(hCursor) : NULL;
|
||||
}
|
||||
|
||||
const INT crosshair1 = 6, crosshair2 = crosshair1 - 2;
|
||||
const INT width = diameter + 2 * crosshair1, height = diameter + 2 * crosshair1;
|
||||
const DWORD hotX = width / 2, hotY = height / 2;
|
||||
|
||||
HDC hdcScreen = ::GetDC(NULL);
|
||||
if (!hdcScreen)
|
||||
return NULL;
|
||||
HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
|
||||
if (!hdcMem)
|
||||
{
|
||||
::ReleaseDC(NULL, hdcScreen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RECT rc = { 0, 0, width, height };
|
||||
|
||||
// Create the AND mask bitmap. This must be monochrome (1bpp):
|
||||
// white bits are transparent, black bits are opaque.
|
||||
HBITMAP hbmMask = ::CreateBitmap(width, height, 1, 1, NULL);
|
||||
if (hbmMask)
|
||||
{
|
||||
HBITMAP hbmOld = (HBITMAP)::SelectObject(hdcMem, hbmMask);
|
||||
|
||||
// Fill with white brush
|
||||
::FillRect(hdcMem, &rc, (HBRUSH)::GetStockObject(WHITE_BRUSH));
|
||||
|
||||
if (!is_rubber)
|
||||
{
|
||||
// Draw crosshair with white pen
|
||||
::SelectObject(hdcMem, (HPEN)::GetStockObject(WHITE_PEN));
|
||||
::MoveToEx(hdcMem, 0, hotY, NULL);
|
||||
::LineTo(hdcMem, crosshair2, hotY);
|
||||
::MoveToEx(hdcMem, width - crosshair2, hotY, NULL);
|
||||
::LineTo(hdcMem, width, hotY);
|
||||
::MoveToEx(hdcMem, hotX, 0, NULL);
|
||||
::LineTo(hdcMem, hotX, crosshair2);
|
||||
::MoveToEx(hdcMem, hotX, height - crosshair2, NULL);
|
||||
::LineTo(hdcMem, hotX, height);
|
||||
}
|
||||
|
||||
// Draw brush or erase with black color
|
||||
if (is_rubber)
|
||||
Erase(hdcMem, hotX, hotY, hotX, hotY, RGB(0, 0, 0), radius + 1);
|
||||
else
|
||||
Brush(hdcMem, hotX, hotY, hotX, hotY, RGB(0, 0, 0), style, diameter);
|
||||
|
||||
if (is_rubber)
|
||||
InflateRect(&rc, -1, -1);
|
||||
|
||||
::SelectObject(hdcMem, hbmOld);
|
||||
}
|
||||
|
||||
// Create the color (XOR) bitmap
|
||||
HBITMAP hbmColor = ::CreateCompatibleBitmap(hdcScreen, width, height);
|
||||
if (hbmColor)
|
||||
{
|
||||
HBITMAP hbmOld = (HBITMAP)::SelectObject(hdcMem, hbmColor);
|
||||
|
||||
// Fill with black brush
|
||||
::FillRect(hdcMem, &rc, (HBRUSH)::GetStockObject(BLACK_BRUSH));
|
||||
|
||||
if (is_rubber)
|
||||
{
|
||||
// Draw for rubber border
|
||||
INT avg = (GetRValue(color) + GetGValue(color) + GetBValue(color)) / 3;
|
||||
COLORREF color2 = (avg > 255 / 2) ? RGB(0, 0, 0) : RGB(255, 255, 255);
|
||||
Erase(hdcMem, hotX, hotY, hotX, hotY, color2, radius + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw crosshair with white pen
|
||||
::SelectObject(hdcMem, (HPEN)::GetStockObject(WHITE_PEN));
|
||||
::MoveToEx(hdcMem, 0, hotY, NULL);
|
||||
::LineTo(hdcMem, crosshair2, hotY);
|
||||
::MoveToEx(hdcMem, width - crosshair2, hotY, NULL);
|
||||
::LineTo(hdcMem, width, hotY);
|
||||
::MoveToEx(hdcMem, hotX, 0, NULL);
|
||||
::LineTo(hdcMem, hotX, crosshair2);
|
||||
::MoveToEx(hdcMem, hotX, height - crosshair2, NULL);
|
||||
::LineTo(hdcMem, hotX, height);
|
||||
}
|
||||
|
||||
// Draw brush or erase with color
|
||||
if (is_rubber)
|
||||
Erase(hdcMem, hotX, hotY, hotX, hotY, color, radius);
|
||||
else
|
||||
Brush(hdcMem, hotX, hotY, hotX, hotY, color, style, diameter);
|
||||
|
||||
::SelectObject(hdcMem, hbmOld);
|
||||
}
|
||||
|
||||
::ReleaseDC(NULL, hdcScreen);
|
||||
::DeleteDC(hdcMem);
|
||||
|
||||
ICONINFO ii = { FALSE, hotX, hotY, hbmMask, hbmColor };
|
||||
HCURSOR hCursor = (HCURSOR)::CreateIconIndirect(&ii);
|
||||
|
||||
::DeleteObject(hbmMask);
|
||||
::DeleteObject(hbmColor);
|
||||
|
||||
return hCursor;
|
||||
}
|
||||
|
||||
void CStyledCursor::SetStyle(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber)
|
||||
{
|
||||
if (m_hCursor && m_style == style && m_radius == radius && m_color == color &&
|
||||
m_is_rubber == is_rubber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_hCursor)
|
||||
DestroyCursor(m_hCursor);
|
||||
|
||||
m_hCursor = CreateStyledCursor(style, radius, color, is_rubber);
|
||||
m_style = style;
|
||||
m_radius = radius;
|
||||
m_color = color;
|
||||
m_is_rubber = is_rubber;
|
||||
}
|
||||
|
||||
CCanvasWindow::CCanvasWindow()
|
||||
: m_drawing(FALSE)
|
||||
, m_hitCanvasSizeBox(HIT_NONE)
|
||||
@@ -641,6 +773,21 @@ LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL
|
||||
case TOOL_AIRBRUSH:
|
||||
::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_AIRBRUSH)));
|
||||
break;
|
||||
case TOOL_RUBBER:
|
||||
{
|
||||
m_hRubberCursor.SetStyle(BrushStyleSquare, toolsModel.GetRubberRadius(),
|
||||
paletteModel.GetBgColor(), TRUE);
|
||||
m_hRubberCursor.SetCursor();
|
||||
break;
|
||||
}
|
||||
case TOOL_BRUSH:
|
||||
{
|
||||
m_hBrushCursor.SetStyle(toolsModel.GetBrushStyle(),
|
||||
toolsModel.GetBrushWidth() / 2,
|
||||
paletteModel.GetFgColor(), FALSE);
|
||||
m_hBrushCursor.SetCursor();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
::SetCursor(::LoadCursorW(NULL, (LPCWSTR)IDC_CROSS));
|
||||
}
|
||||
|
||||
@@ -3,10 +3,42 @@
|
||||
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
|
||||
* PURPOSE: Providing the canvas window class
|
||||
* COPYRIGHT: Copyright 2015 Benedikt Freisen <[email protected]>
|
||||
* Copyright 2026 Katayama Hirofumi MZ <[email protected]>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
enum BrushStyle : int;
|
||||
|
||||
class CStyledCursor
|
||||
{
|
||||
public:
|
||||
~CStyledCursor()
|
||||
{
|
||||
if (m_hCursor)
|
||||
::DestroyCursor(m_hCursor);
|
||||
}
|
||||
|
||||
void SetStyle(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber);
|
||||
|
||||
void SetCursor()
|
||||
{
|
||||
if (m_hCursor)
|
||||
::SetCursor(m_hCursor);
|
||||
}
|
||||
|
||||
operator HCURSOR() const { return m_hCursor; }
|
||||
|
||||
protected:
|
||||
HCURSOR m_hCursor = NULL;
|
||||
BrushStyle m_style;
|
||||
INT m_radius = -1;
|
||||
COLORREF m_color = CLR_INVALID;
|
||||
BOOL m_is_rubber = FALSE;
|
||||
|
||||
static HCURSOR CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber);
|
||||
};
|
||||
|
||||
class CCanvasWindow : public CWindowImpl<CCanvasWindow>
|
||||
{
|
||||
public:
|
||||
@@ -56,6 +88,8 @@ public:
|
||||
|
||||
protected:
|
||||
HITTEST m_hitCanvasSizeBox;
|
||||
CStyledCursor m_hBrushCursor;
|
||||
CStyledCursor m_hRubberCursor;
|
||||
POINT m_ptOrig; // The origin of drag start
|
||||
CRect m_rcResizing; // Resizing rectagle
|
||||
|
||||
|
||||
@@ -72,10 +72,17 @@ INT ToolsModel::GetBrushWidth() const
|
||||
return m_brushWidth;
|
||||
}
|
||||
|
||||
void ToolsModel::SendSetCursor()
|
||||
{
|
||||
canvasWindow.SendMessage(WM_SETCURSOR, (WPARAM)(HWND)canvasWindow,
|
||||
MAKELPARAM(HTCLIENT, WM_MOUSEMOVE));
|
||||
}
|
||||
|
||||
void ToolsModel::SetBrushWidth(INT nBrushWidth)
|
||||
{
|
||||
m_brushWidth = nBrushWidth;
|
||||
NotifyToolSettingsChanged();
|
||||
SendSetCursor();
|
||||
imageModel.NotifyImageChanged();
|
||||
}
|
||||
|
||||
@@ -128,6 +135,7 @@ BrushStyle ToolsModel::GetBrushStyle() const
|
||||
void ToolsModel::SetBrushStyle(BrushStyle nBrushStyle)
|
||||
{
|
||||
m_brushStyle = nBrushStyle;
|
||||
SendSetCursor();
|
||||
NotifyToolSettingsChanged();
|
||||
}
|
||||
|
||||
@@ -199,6 +207,7 @@ int ToolsModel::GetRubberRadius() const
|
||||
void ToolsModel::SetRubberRadius(int nRubberRadius)
|
||||
{
|
||||
m_rubberRadius = nRubberRadius;
|
||||
SendSetCursor();
|
||||
NotifyToolSettingsChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ enum TOOLTYPE
|
||||
TOOL_MAX = TOOL_RRECT,
|
||||
};
|
||||
|
||||
enum BrushStyle
|
||||
enum BrushStyle : int
|
||||
{
|
||||
BrushStyleRound,
|
||||
BrushStyleSquare,
|
||||
@@ -81,6 +81,7 @@ private:
|
||||
ToolBase *m_pToolObject;
|
||||
|
||||
ToolBase *GetOrCreateTool(TOOLTYPE nTool);
|
||||
void SendSetCursor();
|
||||
|
||||
public:
|
||||
ToolsModel();
|
||||
|
||||
Reference in New Issue
Block a user