第4章,[标签 Win32] :SysMets3 程序讲解05,水平滚动

专栏导航

上一篇:第4章,[标签 Win32] :SysMets3 程序讲解04,垂直滚屏重绘

回到目录

下一篇:无

本节前言

对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。

我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。

本节,我们需要结合着 SysMets3 的代码,来讲解知识。关于 SysMets3 的代码,请参考如下课节。

参考课节:SysMets3,待修改

想要学习好本节,最好呢,你对之前的几节已经掌握了。

在这里,我姑且推荐几个参考课节。

参考课节:第4章,[标签 Win32] :SysMets3 程序讲解01

参考课节:第4章,[标签 Win32] :SysMets3 程序讲解02,待修改

参考课节:第4章,[标签 Win32] :SysMets3 程序讲解03,垂直滚屏原理,待修改

参考课节:第4章,[标签 Win32] :SysMets3 程序讲解04,垂直滚屏重绘,待修改

本节,我们要去讲解的,是 WM_PAINT 消息处理代码中,对于水平滚动条的处理部分。

一. iHorzPos 的设置与滚屏原理讲述

我们来看一看 WM_PAINT 消息处理的开头几行的代码。
图1

在开头几行,最主要的 ,是做了两件事,那就是通过调用 GetScrollInfo 函数,获取垂直滚动条和水平滚动条的当前位置值,分别将这两个位置值保存在变量 iVertPos 和 iHorzPos 中。如图1 的227 行与 232 行所示。

此时所获得的滚动条位置,它是滚动条的最新的位置。

在这里,我以水平滚动条为例,来说明这个位置值的含义。

一般地,我们在使用一个程序,比如一个文本编辑器,或者是记事本,或者是 Notepad++,或者是 VS Code,当我们在使用滚动条来翻看文本的时候,在我们拖动滚动条的同时,文本就跟着卷动了。

而在程序的运行的时候,程序的运行机理,与我们所看到的,可能不是一致的。

在这里,我以逻辑的思路,将其暂且分解开来。这种分解,不见得对,但是呢,暂时这么来分解着,还是可以有助于我们的理解。

另外呢,为了理解与讲解的方便,我们假定,程序中使用的文本字体,为等宽字体,所有字符的宽度都一致。

当我们单击水平滚动条的滑块右方的滚动条区域的时候,在程序的 WM_HSCROLL 消息的处理代码中,已经是设置好了滑块的新位置值,并且已经通过调用 SetScrollInfo 函数,并在其中的最后一个参数中指定 TRUE,使得滚动条已经被重绘了。当滚动条重绘好了以后,滚动条滑块已经被绘制在新位置上了。在这个时候,滚动条滑块已经处在新位置上了,但是文本内容尚未完成滚屏工作。

所以呢,对于单击水平滚动条的滑块右方的滚动条空白位置这一操作,其运行效果要分为两部分。

第一阶段,滚动条滑块要变到新位置上,而客户区内容尚未进行滚屏工作。

第二阶段,客户区内容进行滚屏活动。

水平滚动条的滚屏工作,有一部分是在 WM_HSCROLL 消息代码中进行的,也就是在 ScrollWindow 函数中进行的,另一部分,则是在 WM_PAINT 消息处理代码中进行。

对于滚屏工作,根据上一节的讲解,我们也可以将其分为了两个阶段。第一阶段,客户区的某些可见内容进行滚屏,滚屏后依然显示在客户区中。滚屏中还会出现新的列,新的列显示为空白列。第二阶段,对空白列进行重绘。

不过,佩措尔德先生在编写水平滚动条的代码的时候,他在第二阶段,简化了代码逻辑,不是单独针对空白列进行重绘,而是针对某一行的所有字符进行重绘。

我们的代码,也采用了佩措尔德先生的做法。

也就是说,在我们的程序里面,水平滚动条的处理逻辑分为两个阶段。第一阶段,客户区的某些可见内容进行滚屏,滚屏后依然显示在客户区中。滚屏中还会出现新的列,新的列显示为空白列。第二阶段,对该行文本的所有字符进行重绘。

我们接着往下讲。

在我们的理解中,我们可以认为,在 WM_HSCROLL 消息处理代码中,滚屏操作完成了第一阶段的任务,即客户区中原来的部分可见内容的滚屏与产生新的空白行。第一阶段的工作,是在调用 ScrollWindow 函数时进行并完成的。而滚屏的第二阶段的工作,需要在 WM_PAINT 消息处理代码中进行。

当程序运行到 WM_PAINT 的处理代码,刚刚运行完图1 所示的 232 行代码行的时候,此时,iHortPos 获取了滚动条滑块的位置值。这个位置值,它不是单击滑块右方的滚动条空白区域之前的旧位置值,而是单击了滑块右方的滚动条空白区域以后,已经重绘了滚动条之后的滑块的新位置值。只是,在这个时候,滑块已经是变动到新位置了,而由滚屏动作所产生的空白行的部分,尚未进行重绘。

二. 文本宽度假定

一般来讲,在程序中绘制文本的时候,我们既可以使用等款字体,也可以使用变宽字体。并且,很多时候,我们使用的,都是变宽字体。但是呢,在本节的讲解过程中,以及在你的理解活动中,我们将其简化处理,而将本节所使用的字体,看作是等宽字体,所有文本字符的宽度都一致。

这一点,第一分节其实已经是说过了的。不过呢,在这里,我还是再次来强调一遍。

我们接着往下看。

三. for 循环中的文本重绘代码

我们来看代码截图。
图2

图3 中,for 循环的开始,是设置 x 和 y,而这个 x 和 y 分别作为245 行的的 TextOut 调用的 x 实参和 y 实参。再往后的几个 TextOut 函数调用,我们在 SysMets1 和 SysMets2 里面是讲过的。因此,我们本节不详细展开 249 行到 259 行,只重点关注着 245 行的 TextOut 调用。

245 行的 TextOut 函数调用中,x 和 y 实参位置,传过去的,便是 242 和 243 行设置的 x 和 y 变量。

242 行设置的 x 变量,它与水平滚动条有关。243 行设置的 y 变量,它与垂直滚动条有关。本节,我们不讲垂直滚动条的内容。垂直滚动条的内容,我们之前已经讲过了。忘了的,请参考本节前言中列出的相关课节。

因此,在本节,对于 y,我们可以忽略它。或者,你可以在 245 的 TextOut 函数调用中,将 y 看作是 0 就可以了。在进一步地,你可以将 245 到 259 行中出现的 x,全都看作是 0 。

总之,本节,我们不管 y 变量,只探讨 x 变量的使用。

四. x 的初始化

我们来看一看图2 中 242 行的设置 x 的代码。

复制代码
x = cxChar * (1 - iHorzPos);

其中的【1 - iHorzPos】,它是阿拉伯数字 1 减去 iHorzPos,而不是英文字母 L 的小写 l 减去 iHorzPos 。

这个 1 有啥用?

我们将这个程序改一改,将其中的 1 改为 10,然后我们重新编译并运行程序,结果如下。
图3

关闭程序。然后呢,再将 刚才的10 改为 30,然后重新编译运行程序,结果如下。
图4

关闭程序,并将刚刚的 30,改回 1,如下图所示。
图5

我们接着讲。

比较一下图3 与图4,我们可以看出来,1 也好,10 也好,30 也好,它所管的是,在初始显示的时候,文本的第一列的字符,距离客户区边的字符数。如果这个数是1,则初始显示时,第一列字符距离客户区左边的距离为 1 个字符。如果这个数是10,则初始显示时,第一列字符距离客户区左边的距离为 10 个字符。如果这个数是30,则初始显示时,第一列字符距离客户区左边的距离为 30 个字符。

为什么是这样呢?

我们之前说过,242 行的 iHorzPos 为水平滚动条的新位置值。

我们暂且假定,【x = cxChar * (1 - iHorzPos);】。

因为,当程序初始运行,第一次进入 WM_PAINT消息处理代码时,到了程序的第 242 行,iHorzPos 的值为 0 。因为刚刚创建的滚动条,其滑块位置值,就是 0 啊。当 iHorzPos 为 0 时,那么,它左边的 num 是多少,则【num - iHorzPos】就等于多少,x 便会等于多少个 cxChar 的值。而图2 的 245 行对应的初始 TextOut 调用中,便是用这个 x 值作为实参的。

因此,num 等于多少,初始显示时,文本的第一列的字符,距离客户区左边缘,便是多少个字符宽度。

这个问题,就算是暂且说完了,我们接着往下看。

五. 水平重绘

说完了初始化显示,我们再来看一般化的水平重绘。

在初始显示时, iHorzPos 等于 0 。而在使用程序的过程中,它的值就不一定了。可能等于 0,可能等于 6, 9, 12,等等。

假定,在某一刻,iHorzPos 的值等于 3 。在这个假定之下,我们来考察程序的显示情况。

遵循本篇文章的第二分节的假定,程序中使用等宽字体,所有文本字符的宽度一致。

我们再次贴出 x 的赋值代码。

复制代码
x = cxChar * (1 - iHorzPos);

已知 iHorzPos == 3,且所有文本字符宽度一致。

cxChar 表示,在当前设备环境中,一个字符的平均宽度。当我们假定程序使用等宽字体,所有文本字符的宽度一致时,我们可以将 cxChar 右边的因子,1 - iHorzPos,视为客户区的某某号列字符位置值。

在这种情况下,x == cxChar * (1 - 3) = cxChar * (-2),这里计算出来的 x 值,它会作为 245 行的 TextOut 中的 x 参数的实参,它代表的是 TextOut 中,文本字符串的起始字符的横坐标值。文本起始字符,我们可以将其称作文本0号字符。而【cxChar * (-2)】,我们可以将其称作客户区 -2 号字符位置。

由此,当 iHorzPos == 3 时,245 行的 TextOut 函数的文本 0 号字符,显示在客户区 -2 号列字符的位置上。客户区的 -2 号列字符位置,位于客户区左边缘的左边,不在客户区范围内,不予显示。

当 iHorzPos == 3 时,245 行的 TextOut 函数的文本 1 号字符,显示在客户区 -1 号列字符的位置上。客户区的 -1 号列字符位置,位于客户区左边缘的左边,不在客户区范围内,不予显示。

当 iHorzPos == 3 时,245 行的 TextOut 函数的文本 2 号字符,显示在客户区 0 号列字符的位置上。客户区的 0 号列字符位置,位于客户区范围内,予以显示。

当 iHorzPos == 3 时,245 行的 TextOut 函数的文本 3 号字符,显示在客户区 1 号列字符的位置上。客户区的 1 号列字符位置,位于客户区范围内,予以显示。

就这样地,TextOut 中的每一个文本字符,起始字符编号为 0,每一个后续字符依次编号加 1,它们挨个地计算其显示横坐标。如果位图客户区显示范围内,就予以显示。如果不在客户区显示范围内,则不予显示。

我们使用使用程序的使用,通过拖动滚动条滑块,或者是单击滑块左右两边的滚动条区域,或者单击向左向右三角号,都可能改变 iHorzPos 的值,导致水平重绘代码被执行。对于任何一个有效的 iHorzPos 值,显示逻辑都差不多。都是说,对 TextOut 中的文本字符予以编号,从 起始字符 的 0 编号 开始,后续字符依次加 1 编号,然后挨个地计算其在客户区中显示的横坐标,位于客户区范围内的,就予以显示。不属于客户区范围的,就不予显示。

结束语

关于水平滚动条的滚屏工作与垂直滚动条的滚屏工作,个人认为,水平滚动条的原理更简单一些。不过,对你来讲,也许,垂直滚动条更简单。因人而异吧。

对我来讲,水平滚动条与垂直滚动条的讲解任务,都不简单。

好多的课节,讲起来,都会有一定的难度啊。

想要当好一个讲师,讲好课程,也不容易啊。

希望你能够学好滚动条的滚屏运作机制。

专栏导航

上一篇:第4章,[标签 Win32] :SysMets3 程序讲解04,垂直滚屏重绘

回到目录

下一篇:无

相关推荐
lihao lihao2 小时前
进程地址空间
数据结构·c++·算法
Byte不洛2 小时前
LeetCode双指针经典题
c++·算法·leetcode·双指针
Tanecious.2 小时前
蓝桥杯备赛:Day7- P10424 [蓝桥杯 2024 省 B] 好数
c++·蓝桥杯
Albert Edison2 小时前
【C++11】特殊类设计
开发语言·c++·单例模式·饿汉模式·懒汉模式
代码改善世界2 小时前
【C++初阶】vector 核心接口和模拟实现
开发语言·c++
今晚打老虎2 小时前
限时回归了
c++
老四啊laosi2 小时前
[C++进阶] 22. unordered_set && unordered_map使用
c++·unordered_map·unordered_set
宵时待雨2 小时前
C++笔记归纳20:智能指针
开发语言·c++·笔记
Felven2 小时前
A. Redstone?
c语言