/** @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();
}
}

Duilib的圆环形 进度条 实现(网易云信版本)的更多相关文章

  1. 图解CSS3制作圆环形进度条的实例教程

    圆环形进度条制作的基本思想还是画出基本的弧线图形,然后CSS3中我们可以控制其旋转来串联基本图形,制造出部分消失的效果,下面就来带大家学习图解CSS3制作圆环形进度条的实例教程 首先,当有人说你能不能 ...

  2. iOS 开发技巧-制作环形进度条

    有几篇博客写到了怎么实现环形进度条,大多是使用Core Graph来实现,实现比较麻烦且效率略低,只是一个小小的进度条而已,我们当然是用最简单而且效率高的方式来实现. 先看一下这篇博客,博客地址:ht ...

  3. iOS一分钟学会环形进度条

    有几篇博客写到了怎么实现环形进度条,大多是使用Core Graph来实现,实现比较麻烦且效率略低,只是一个小小的进度条而已,我们当然是用最简单而且效率高的方式来实现.先看一下这篇博客,博客地址:htt ...

  4. canvas绘制环形进度条

    <!DOCTYPE html> <html > <head> <meta http-equiv="content-type" conten ...

  5. 【CSS】环形进度条

    效果图 原理剖析 1.先完成这样一个半圆(这个很简单吧) 2.overflow: hidden; 3.在中间定位一个白色的圆形做遮挡 4.完成另一半 5.使用animate配合时间完成衔接 源码 &l ...

  6. canvas环形进度条

    <style> canvas { border: 1px solid red; margin: 100px; }</style> <canvas id="rin ...

  7. html5 canvas绘制环形进度条,环形渐变色仪表图

    html5 canvas绘制环形进度条,环形渐变色仪表图                                             在绘制圆环前,我们需要知道canvas arc() 方 ...

  8. canvas实现半圆环形进度条

    html部分 <canvas id="canvas" width="150" height="150"> <p>抱歉 ...

  9. 【css】如何实现环形进度条

    最近团队的童鞋接到了一个有关环形进度条的需求,想要还原一个native的沿环轨迹渐变进度条的效果,看到这个效果的时候,笔者陷入了沉思.. 环形进度条的效果,最先想到的就是使用CSS利用两个半圆的hac ...

随机推荐

  1. Android native进程间通信实例-socket本地通信篇之——基本通信功能

    导读: 网上看了很多篇有关socket本地通信的示例,很多都是调通服务端和客户端通信功能后就没有下文了,不太实用,真正开发中遇到的问题以及程序稳定性部分没有涉及,代码健壮性不够,本系列(socket本 ...

  2. Java SpringBoot 如何使用 IdentityServer4 作为验证学习笔记

    这边记录下如何使用IdentityServer4 作为 Java SpringBoot 的 认证服务器和令牌颁发服务器.本人也是新手,所以理解不足的地方请多多指教.另外由于真的很久没有写中文了,用词不 ...

  3. Mybatis__模糊查询

    在一个Web工程中,查询功能几乎都要用到姓名模糊查询,,虽然学号,工号等可以最准确最快的定位,但如果清楚信息到连学号,工号都一个数不差,应该也没必要去查询了. 故需要用到一下语句实现模糊查询: sel ...

  4. [Lydsy2017年4月月赛]抵制克苏恩题解

    考试的时候以为就是简单的概率期望题,考完后知道是简单的概率期望DP题,完美爆零. 这道题数据范围很小,很容易让人想到状压,不过貌似没什么可压的.那么只能说明这道题复杂度很高了,状态数组f[o][i][ ...

  5. Latch设计模式

    import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; public class Te ...

  6. ISTQB TTA大纲中提到的参考书目

    在2012版大纲(可以点击这里下载查看)第12页"2.2条件测试"标题上方有这样一句话: 参考[Bath08],[Beizer90],[Beizer95],[Copeland03] ...

  7. 简单的量子算法(一):Hadamard 变换、Parity Problem

    Hadamard Transform Hadamard 变换在量子逻辑门中提过,只不过那时是单量子的Hadamard门,负责把\(|1\rangle\)变成\(|-\rangle\),\(|0\ran ...

  8. 个人永久性免费-Excel催化剂功能第96波-地图数据挖宝之全国天气查询(区域最细可到区县,最长预报4天)

    天气预报的信息,是很普通的大家习以为常的信息,但如果不进行采集,在日常数据分析过程中,就少了非常重要的一个分析维度,如果人手采集整理,工作量巨大.此篇给广大数据分析工作者再次减负,只需简单一键,即可批 ...

  9. Storm之API简介

    Storm之API简介 Component组件 1)基本接口 (1)IComponent接口 (2)ISpout接口 (3)IRichSpout接口 (4)IStateSpout接口 (5)IRich ...

  10. 使用canvas来完成线性渐变和径向渐变的功能

    fillStyle的第二种使用情况就是渐变色的填充.渐变色就分为线性渐变色和径向渐变色.   线性渐变:大致分为两步 这里又会使用到canvas的两个新的函数.   第一步 : 使用一个新的函数cre ...