/** @file CircleProgress.h
* @brief 圓環型進度條控件,圓環中間可以有文本(如85%)
* @copyright (c) 2019-2022, NetEase Inc. All rights reserved
* @author Xuhuajie
* @date 2019/8/14
*/
#ifndef UI_CONTROL_CIRCLEPROGRESS_H_
#define UI_CONTROL_CIRCLEPROGRESS_H_
#pragma once
namespace ui
{
class UILIB_API CircleProgress : public Progress
{
public:
CircleProgress();
/// 重寫父類方法,提供個性化功能,請參考父類聲明
virtual void SetAttribute(const std::wstring& strName, const std::wstring& strValue) override;
virtual void PaintStatusImage(IRenderContext* pRender) override;
virtual void ClearImageCache() override;
/**
* @brief 設置圓形進度條
* @param[in] bCircular 為 true 時設置為圓形滾動條,false 時設置為父級滾動條,默認為 true
* @return 無
*/
void SetCircular(bool bCircular = true);
/**
* @brief 設置遞增方向
* @param[in] bClockwise 為 true 時設置為順時針,false 時設置為逆時針,默認為 true
* @return 無
*/
void SetClockwiseRotation(bool bClockwise = true);
/**
* @brief 設置圓環寬度
* @param[in] nCircleWidth 寬度數值
* @return 無
*/
void SetCircleWidth(int nCircleWidth);
/**
* @brief 設置進度條背景顏色
* @param[in] strColor要設置的背景顏色字符串,該字符串必須在 global.xml 中存在
* @return 無
*/
void SetBackgroudColor(const std::wstring& strColor);
/**
* @brief 設置進度條前景顏色
* @param[in] strColor要設置的前景顏色字符串,該字符串必須在 global.xml 中存在
* @return 無
*/
void SetForegroudColor(const std::wstring& strColor);
/**
* @brief 設置進度條前景漸變顏色,與ForegroudColor同時使用,可以不設置,則無漸變效果
* @param[in] strColor要設置的前景漸變顏色字符串,該字符串必須在 global.xml 中存在
* @return 無
*/
void SetCircleGradientColor(const std::wstring& strColor);
/**
* @brief 設置進度指示移動圖標
* @param[in] sIndicatorImage要設置的圖片
* @return 無
*/
void SetIndicator(const std::wstring& sIndicatorImage);
protected:
bool m_bCircular;
bool m_bClockwise;
int m_nCircleWidth;
DWORD m_dwBackgroundColor;
DWORD m_dwForegroundColor;
DWORD m_dwGradientColor;
//Image m_IndicatorImage; //使用image對象,無法滿足需求,需要設置矩陣變換
Gdiplus::Image* m_pIndicator; //此類目前維護資源管理
std::wstring m_sIndicatorImage;
};
} // namespace ui
#endif // UI_CONTROL_CIRCLEPROGRESS_H_
/** @file CircleProgress.cpp
* @brief 圓環型滾動條控件,圓環中間可以有文本(如85%)
* @copyright (c) 2019-2022, NetEase Inc. All rights reserved
* @author Xuhuajie
* @date 2019/8/14
*/
#include "stdafx.h"
#include "CircleProgress.h"
#include "shlwapi.h"
namespace ui
{
CircleProgress::CircleProgress() :
m_bCircular(true),
m_bClockwise(true),
m_nCircleWidth(1),
m_dwBackgroundColor(0),
m_dwForegroundColor(0),
m_dwGradientColor(0),
m_pIndicator(nullptr)
{
}
void CircleProgress::SetAttribute(const std::wstring& srName, const std::wstring& strValue)
{
if (srName == _T("circular")) SetCircular(strValue == _T("true"));
else if (srName == _T("circlewidth")) SetCircleWidth(_ttoi(strValue.c_str()));
else if (srName == _T("indicator")) SetIndicator(strValue);
else if (srName == _T("clockwise")) SetClockwiseRotation(strValue == _T("true"));
else if (srName == _T("bgcolor")) {
LPCTSTR pValue = strValue.c_str();
while (*pValue > _T('\0') && *pValue <= _T(' ')) pValue = ::CharNext(pValue);
SetBackgroudColor(pValue);
}
else if (srName == _T("fgcolor")) {
LPCTSTR pValue = strValue.c_str();
while (*pValue > _T('\0') && *pValue <= _T(' ')) pValue = ::CharNext(pValue);
SetForegroudColor(pValue);
}
else if (srName == _T("gradientcolor")) {
LPCTSTR pValue = strValue.c_str();
while (*pValue > _T('\0') && *pValue <= _T(' ')) pValue = ::CharNext(pValue);
SetCircleGradientColor(pValue);
}
else Progress::SetAttribute(srName, strValue);
}
void CircleProgress::PaintStatusImage(IRenderContext* pRender)
{
Progress::PaintStatusImage(pRender);
if (m_bCircular)
{
//目前IRenderContext還有很多GDI+接口未實現,暫時直接用gdi+畫圖了
//以后可能會調整:需實現1、DrawArc 2、Pen增加brush(漸變)入參 3、可以自由設置Graphics屬性
int direction = m_bClockwise ? 1 : -1; //旋轉方向
int bordersize = 1; //弧度寬度目前使用1像素
Gdiplus::Graphics graphics(pRender->GetDC());
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen bgPen(m_dwBackgroundColor, m_nCircleWidth);
// 圓形中心
CPoint center;
center.x = m_rcItem.left + (m_rcItem.right - m_rcItem.left) / 2;
center.y = m_rcItem.top + (m_rcItem.bottom - m_rcItem.top) / 2;
// 控件矩形內的最大正方形的邊界
int side = min(m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top);
//UiRect rcBorder; 仍然存在UiRect 到 RectF的轉換,所以直接用gdi的RectF了
Gdiplus::RectF rcBorder;
rcBorder.X = center.x - side / 2;
rcBorder.Y = center.y - side / 2;
rcBorder.Width = rcBorder.Height = side;
Gdiplus::RectF outer = rcBorder;
if (m_pIndicator) {
outer.Inflate(-1.0F *m_pIndicator->GetWidth() / 2, -1.0F * m_pIndicator->GetWidth() / 2);
}
else
{
outer.Inflate(-0.5 * m_nCircleWidth, -0.5 * m_nCircleWidth);
}
outer.Inflate(-1, -1);
if (m_dwGradientColor == 0)
{
//不使用漸變色,直接用前景色鋪滿
Gdiplus::Pen fgPen(m_dwForegroundColor, m_nCircleWidth);
graphics.DrawArc(&bgPen, outer, 270, 360); //270從最上面開始遞增,設為0的話,是最右邊開始
graphics.DrawArc(&fgPen, outer, 270, direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin));
}
else
{
Gdiplus::REAL factors[4] = { 0.0f, 0.4f, 0.6f, 1.0f };
Gdiplus::REAL positions[4] = { 0.0f, 0.2f, 0.8f, 1.0f };
Gdiplus::LinearGradientBrush lgbrush(rcBorder, m_dwForegroundColor, m_dwGradientColor, Gdiplus::LinearGradientModeVertical);
lgbrush.SetBlend(factors, positions, 4);
graphics.DrawArc(&bgPen, outer, 270, 360);
Gdiplus::Pen fgPen(&lgbrush, m_nCircleWidth);
graphics.DrawArc(&fgPen, outer, 270, direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin));
}
//畫旋轉指示器圖標,需要用到矩陣
if (m_pIndicator)
{
Gdiplus::Matrix matrix;
matrix.RotateAt(direction * 360 * (m_nValue - m_nMin) / (m_nMax - m_nMin), Gdiplus::PointF(center.x, center.y), Gdiplus::MatrixOrderAppend);
graphics.SetTransform(&matrix);
Gdiplus::RectF rectf;
rectf.X = center.x - m_pIndicator->GetWidth() / 2;
rectf.Y = outer.Y + bordersize / 2 -m_pIndicator->GetHeight() / 2;
rectf.Width = m_pIndicator->GetWidth();
rectf.Height = m_pIndicator->GetHeight();
graphics.DrawImage(m_pIndicator, rectf);
}
}
}
void CircleProgress::ClearImageCache()
{
__super::ClearImageCache();
if (m_pIndicator)
{
delete m_pIndicator;
m_pIndicator = nullptr;
}
}
void CircleProgress::SetCircular(bool bCircular /*= true*/)
{
m_bCircular = bCircular;
Invalidate();
}
void CircleProgress::SetClockwiseRotation(bool bClockwise /*= true*/)
{
if (bClockwise != m_bClockwise)
{
m_bClockwise = bClockwise;
if (m_pIndicator)
{
//已經旋轉了圖片,旋轉到相反的方向
m_pIndicator->RotateFlip(Gdiplus::Rotate180FlipNone);
}
}
}
void CircleProgress::SetCircleWidth(int nCircleWidth)
{
m_nCircleWidth = nCircleWidth;
Invalidate();
}
void CircleProgress::SetBackgroudColor(const std::wstring& strColor)
{
m_dwBackgroundColor = GlobalManager::GetTextColor(strColor);
ASSERT(m_dwBackgroundColor != 0);
Invalidate();
}
void CircleProgress::SetForegroudColor(const std::wstring& strColor)
{
m_dwForegroundColor = GlobalManager::GetTextColor(strColor);
ASSERT(m_dwForegroundColor != 0);
Invalidate();
}
void CircleProgress::SetIndicator(const std::wstring& sIndicatorImage)
{
if (m_sIndicatorImage != sIndicatorImage)
{
m_sIndicatorImage = sIndicatorImage;
if (m_pIndicator)
{
delete m_pIndicator;
m_pIndicator = nullptr;
}
std::wstring imagepath = m_sIndicatorImage;
if (!::PathFileExistsW(imagepath.c_str())) {
imagepath = GlobalManager::GetResourcePath() + m_pWindow->GetWindowResourcePath() + imagepath;
}
if (!::PathFileExistsW(imagepath.c_str())) {
return;
}
m_pIndicator = new Gdiplus::Image(imagepath.c_str());
Gdiplus::Status state = m_pIndicator->GetLastStatus();
if (Gdiplus::Ok == state)
{
// 假定圖片指向上
m_pIndicator->RotateFlip(m_bClockwise ? Gdiplus::Rotate90FlipNone : Gdiplus::Rotate270FlipNone);
Invalidate();
}
}
}
void CircleProgress::SetCircleGradientColor(const std::wstring& strColor)
{
m_dwGradientColor = GlobalManager::GetTextColor(strColor);
ASSERT(m_dwGradientColor != 0);
Invalidate();
}
}