Duilib 源码分析之 CScrollBarUI 篇
来源:互联网 发布:淘宝流量推广工具 编辑:程序博客网 时间:2024/06/04 00:40
Scrollbar 的创建时机是在解析 UIContainer
类型的控件时,发现有 hscrollbar
或 vscrollbar
时,调用 void CContainerUI::EnableScrollBar(bool bEnableVertical = true, bool bEnableHorizontal = false)
实现,请关注下方法中的以下代码:
- void CScrollBarUI::SetHorizontal(bool bHorizontal = true) 设置 Scrollbar 的类型,依赖于当前解析的是
hscrollbar
属性还是vscrollbar
- void CScrollBarUI::SetOwner(CContainerUI* pOwner) 设置 Scrollbar 所属的父控件
- void CControlUI::SetManager(CPaintManagerUI* pManager, CControlUI* pParent, bool bInit = true) 关联当前
CPaintManagerUI
对象 - CControlUI* CControlUI::ApplyAttributeList(LPCTSTR pstrList) 一般来说我们会将共通的 Scrollbar 属性写在 xml 的 Default 属性中,此时通过调用
m_pManager->GetDefaultAttributeList(_T("HScrollBar"))
获取对应 Scrollbar 的属性列表并设置到当前 Scrollbar 中
Scrollbar 的位置设置是在所属父控件执行 SetPos
时,调用 void ProcessScrollBar(RECT rc, int cxRequired, int cyRequired)
实现,由于代码稍长且比较简单,在此不再列出,只简单说一下逻辑,以横向滚动条为例,若有不明白的地方可以留言
cxRequired
代表滚动条所属控件横向所需大小,cxRequired
是在所属控件SetPos
过程中遍历所有子控件后确定,也就是从最左端的子控件到最右端子控件共有多长- 若
cxRequired
不大于所属控件的宽度,则不再需要横向滚动条 - 在符合滚动条显示条件的情况下,确定 Scrollbar 的位置,且调用
void SetScrollRange(int nRange)
函数设置 Scrollbar 的范围,nRange
的值为cxRequired
- 所属控件的宽度(减掉inset
的left
和right
后的宽度)
接下来说一下 Scrollbar 最重要的部分,关于位移相关的逻辑,关键的两个方法为 SetPos
和 DoEvent
,接下来详细介绍一下
void DoEvent(TEventUI& event)
m_nLastScrollOffset
鼠标拖动滚动条时单位时间内,父控件需位移的距离,单位时间指的是m_pManager->SetTimer(this, DEFAULT_TIMERID, 50U)
中的 50ms。 关于m_nLastScrollOffset
的计算代码如下(竖向滚动条):int vRange = m_rcItem.bottom - m_rcItem.top - m_rcThumb.bottom + m_rcThumb.top - 2 * m_cxyFixed.cx;if (vRange != 0) m_nLastScrollOffset = (event.ptMouse.y - ptLastMouse.y) * m_nRange / vRange;
vRange
代表滚动条可以移动的距离,也就是滚动条内除了两端的按钮,和可拖动的条状长度外剩下的长度。比例关系为: 滚动条可以移动的距离(vRange
) / 父控件可以移动的距离(m_nRange
) = 滚动条已经移动的距离(event.ptMouse.y - ptLastMouse.y
) / 父控件已经移动的距离(m_nLastScrollOffset
)。 由此得出上述计算方法m_nScrollRepeatDelay
鼠标持续按下两端的按钮或空白区域时, Timer 的执行次数。m_nScrollRepeatDelay
相关的代码——if( event.Type == UIEVENT_TIMER && event.wParam == DEFAULT_TIMERID )
代码段中, 目的是为了实现持续按下时,不断地移动滚动条和父控件。 在刚按下时,首先位移一次,且在m_nScrollRepeatDelay > 6
的情况下,也就是经过 300ms 后,开始持续位移鼠标点击两端按钮,和鼠标点在空白区域时,父控件的位移大小不一样
点击在按钮时,若是竖向滚动条,代码如下:
void CContainerUI::LineDown(){int cyLine = GetScrollStepSize();if (cyLine == 0) { cyLine = 8;if( m_pManager ) cyLine = m_pManager->GetDefaultFontInfo()->tm.tmHeight + 8;}SIZE sz = GetScrollPos();sz.cy += cyLine;SetScrollPos(sz);}
若父控件设置了
scrollstepsize
属性,则移动对应的距离。否则移动m_pManager->GetDefaultFontInfo()->tm.tmHeight + 8
。
若是横向滚动条,则移动m_nLineSize
, 默认为 8, 可以由滚动条的属性linesize
控制- 若点击在空白区域,则移动的距离为父控件可见区域的大小,相当于翻页
- 点击在可拖动的区域上时,会设置一个关键是属性
UISTATE_CAPTURED
, 在鼠标松开会删除此属性。 之后在响应 Timer 时若发现有此属性,则认为是用户在拖动滚动条
void SetPos(RECT rc, bool bNeedInvalidate = true)
这个方法内算法分为横向和纵向两种,代码不难且比较长,不再详细介绍。这里解释一下以下代码:(横向为例)int cxThumb = cx * (rc.right - rc.left) / (m_nRange + rc.right - rc.left);if( cxThumb < m_cxyFixed.cy ) cxThumb = m_cxyFixed.cy;m_rcThumb.left = m_nScrollPos * (cx - cxThumb) / m_nRange + m_rcButton1.right;
cxThumb
指可以拖动的部分的长度,这个长度的定义不固定,这里定义的比例关系为: 滚动条的总长度(rc.right - rc.left
) / 父控件内部总长度(m_nRange + rc.right - rc.left
) = 滚动条可拖动长度(cxThumb
) / 滚动条不包括两端按钮的长度(cx
)。 之所以说这个定义不固定,大家可以这样想: 假如cxThumb
比较短,大不了做成拖动比较长的距离,父控件才移动单位距离, 若cxThumb
比较长,则会拖动比较短的距离,父控件就会移动单位距离- 在这部分代码中
cxThumb
最小被设置为m_cxyFixed.cy
。也就是和滚动条等高。 在实际项目中,我曾经碰到过一个很长的 list ,导致纵向滚动条的可拖动部分很短,鼠标不容易选中。后来我就加了一个属性,控制可拖动部分的最小长度。
以上内容介绍了滚动条的位移实现原理,若有不明白的地方可以留言询问。滚动条剩下的内容就是绘制了,这部分就不做具体介绍了,绘制的顺序为: 背景->两端的按钮->可拖动部分->可拖动部分上面的图案->边框
- Duilib 源码分析之 CScrollBarUI 篇
- duilib之源码分析
- duilib之源码分析
- Duilib之源码分析
- duilib之源码分析
- Duilib 源码分析之 xml 加载篇
- Duilib 源码分析之 xml 解析篇
- Duilib 源码分析之字体篇
- Duilib 源码分析之控件 name 篇
- Duilib 源码分析之消息流程篇
- Duilib 源码分析之 CResourceManager 篇
- Duilib 源码分析之 CControlUI 篇
- Duilib 源码分析之 Timer 篇
- Duilib 源码分析之 PaintBorder 篇
- Duilib 源码分析之 ToolTip 篇
- Duilib 源码分析之 Shortcut 篇
- Duilib 源码分析之 CLabelUI 篇
- Duilib 源码分析之 CTextUI 篇
- 错误:ERROR 1290 (HY000): Unknown error 1290
- 重定向解决国外服务器与国内域名备案的问题
- 帮助学习ASP.NET生命周期
- Javascript 引擎工作机制(js层面梳理)
- 在eclipse中使用tomcat服务器出现java.lang.OutOfMemoryError: PermGen space 内存溢出错误的解决方法
- Duilib 源码分析之 CScrollBarUI 篇
- Matlab 的fspecial函数用法 -滤波
- 跟我学aspectj之三 ----- Hello World
- android 控件各种颜色的半透明效果配置
- List<Map<String,Object>>与List<Pojo>各自的优缺点是什么?
- mysql数据自动生成报表软件
- Selenium和Firefox对应版本及注意事项
- Swift之网络编程-请求缓存
- Java并发编程:volatile关键字解析