VAPS XT入门系列18:VAPS XT多界面鼠标操作框架

系列索引:VAPS XT4.2.1入门系列索引

方法可能有很多,但是百度根本搜索不到任何有用的东西,而且VAPS软件贵,软件开发的项目工程也贵,别人不可能随便把工程给你参考。我有幸接触到一个相关的工程,将其拆解后制作一个通用的框架。

本文介绍一个基于鼠标操作的窗口布局框架,布局中的每个窗口都可以随意拖动到任意位置。松开鼠标时,窗口会自动回到原位置。如果松开时鼠标所在的位置有另一个窗口,那么就交换两个窗口的显示的内容。每个窗口支持放大缩小调整。

演示效果

先看一下效果。

VAPS XT鼠标布局框架演示-B站

默认界面为:

default

架构图

在看一下架构图:

arch

  • 最上层的是用户操作,主要是鼠标操作。
  • 之后是界面布局,不同的界面按照不同的排列顺序、不同的大小形成不同的布局,使用时可以随意调整布局
  • 布局中的多个界面有不同的大小,涉及到布局调整,所以本文只涉及四种不同的大小:小、中、大、扩
  • 最底层的是最基本的窗口,本文涉及三个窗口,每个窗口有小中大扩四个不同状态。

交换窗口就是交换窗口的大小状态。

约定说明

在正式开发之前,先做一些约定,以保证后续的开发不会混乱。

  • VAPS XT的数组下标从0开始,界面的ID也从0开始,避免操作异常。
  • 布局从左向右,从下到上进行ID排序

id

  • 1为小,2为中等,3为最大,4为扩展
  • 如果修改了工程的组件名称,整个工程内所有相关的组件和代码度都被删除,注意保存。
  • 如果你有更好的方法或者建议可以发邮件给我交流交流。

数据结构

ExchangeIDStruct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dataDescription>
<dataDescription name="ExchangeIDStruct">
<field>
<name>OldID</name>
<type>char</type>
<cardinality>1</cardinality>
</field>
<field>
<name>NewID</name>
<type>char</type>
<cardinality>1</cardinality>
</field>
</dataDescription>

此结构包含两个char型数据,用于表示两个ID,在两个界面交换的时候使用。

ModulePara

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dataDescription>

<dataDescription name="ModulePara">

<field>
<name>Module</name>
<type>ushort</type>
<cardinality>1</cardinality>
</field>

<field>
<name>Mode</name>
<type>ushort</type>
<cardinality>1</cardinality>
</field>

</dataDescription>

ModulePara结构包含两个数字,Module为使用的模块即后面的1/2/3号图像对象,Mode为使用的对象大小即后续的小大中扩。

LayoutStatus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dataDescription>

<dataDescription name="LayoutStatus">

<field>
<name>CurModulePara</name>
<type>ModulePara</type>
<cardinality>1</cardinality>
</field>

<field>
<name>LastMode</name>
<type>byte</type>
<cardinality>1</cardinality>
</field>

<field>
<name>FrameID</name>
<type>byte</type>
<cardinality>1</cardinality>
</field>

<field>
<name>PositionX</name>
<type>float</type>
<cardinality>1</cardinality>
</field>

<field>
<name>PositionY</name>
<type>float</type>
<cardinality>1</cardinality>
</field>

<field>
<name>SizeX</name>
<type>float</type>
<cardinality>1</cardinality>
</field>

<field>
<name>SizeY</name>
<type>float</type>
<cardinality>1</cardinality>
</field>

</dataDescription>

ID为FrameID的图像对象的尺寸,所显示的模块和大小,所在的位置。

LayoutStatusArray

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dataDescription>

<dataDescription name="LayoutStatusArray">

<field>
<name>ValidCount</name>
<type>byte</type>
<cardinality>1</cardinality>
</field>

<field>
<name>Lay</name>
<type>LayoutStatus</type>
<cardinality>9</cardinality>
</field>

</dataDescription>

当前布局的所有图像对象的尺寸、位置、显示模块和大小等等,此数据结构在交换判断是需要用到。

将所有的数据结构保存为*.dd文件,然后导入到工程里面(导入时注意先后顺序)。

ds

并通过LayoutStatusArray创建一个新类LayoutStatusArrayData

data

三个基本界面

本文用到三个界面组,每个界面组包含小中大扩展等四个不同的尺寸状态。

先创建空白的项目,然后添加一个子文件夹用于存放这些基本界面

displayframe

在DisplayFrame子目录下新建一个子目录,名称为One。

one

在One目录中添加四个图像对象(Graphical Object)分别对应大中小扩展四个不同尺寸状态

4ge

每个图像对象都有两个属性

名称类型功能
SizeCoord用于调整界面大小
IsVisibleBool设置界面是否显示

这里的界面大小Size指的是当前界面的大小,一旦设置了就不会再改变。这个Size是对于通用型框架来说的。如果是特定的项目工程,直接写死界面大小就可以了。

在每一个图像对象中添加用于显示的文字组件和图像组件

small

小号的1号窗口大小为320x320,字体大小为40,显示内容为1小。还有一个RectWidthHeight_1用于显示外部边框,不填充内部。

中号大小为640x640,大号为640x960,扩展为1280x640。

按照不同大小不同显示内容创建这样的12个图像对象。

gos

到此为止,用于显示的最基本的图像对象就创建完成了。

基本按钮

在界面的大小切换时需要一个控件来控制,这里我们使用按钮来操作。

创建一个子文件夹

basicui

添加一个图像对象,名称为BtnBack。

属性为:

| 名称 | 类型 | 备注 |
| Size | Coord | 尺寸 |
| IsVisible | Bool | 是否显示 |
| ID | UInt | 当前按钮所在的界面的ID |

设置图片资源,设置数据流

btnback

同时添加一个事件,名称为evBack,参数类型为UInt。

在状态中添加两个状态,一个正常(Normal,即默认状态),一个Clicked(即按钮按下时触发的状态)

state

按钮按下时触发RectActiveArea_1.evPress,会跳转到Clicked状态,同时触发事件.evBack(.ID),松开时RectActiveArea_1.evRelease。

这样的按钮再创建两个BtnExtend,BtnDown,分别触发evExtend/evDown事件

buttons

至此按钮部分结束

窗口组

将同样大小的图像对象添加到同一个窗口组中,此窗口组每次只显示一个对象的一个状态。

小号窗口组

先创建一个图像对象SmallFrames

property

LayoutStatus用于设置SmallFrames当前显示的是哪一个模块(模式都是为小)。

LayoutFromLayout是直接将布局的所有的信息传递过来,在拖拽交换的时候回用到。

在右侧的Events中添加一个事件evSwapFrame,参数为ExchangeIDStruct。

然后向画布添加控件

statue

1

PosBeforeDrag用于保存拖拽之前的位置坐标

SimpleGroup_1用于将子控件形成一个整体操作

RectActiveArea_1用于捕获鼠标事件(点击、按下、拖拽、释放等等)

MutuallyExclusiveGroup_1用于将其下所有子控件形成排他性(一次只能显示一个子控件)

OneSmall_1/TwoSmall_1/ThreeSmall_1是本文用到的三个模块的三个小尺寸的窗口状态。

2

第一行和第二行表示捕获鼠标事件的区域大小和三个模块窗口大小一样

第三行LayoutStatus值有SmallFrames的父窗口传递过来,通过父窗口来控制SmallFrames显示三个模块的哪一个。

3

本部分为状态迁移图,用于捕获鼠标拖拽和释放,释放时判断是否交换。

Normal->Drag

normal2drag

鼠标按下时保存初试位置坐标。

Drag

drag

用于鼠标拖拽时,界面和鼠标一起移动。

Drag->Normal

drag2normal

鼠标拖拽松开时,判断拖拽的界面和鼠标所在的界面能否交换,如果可以交换就发送交换信号。

中等窗口组

在创建一个MainFrames窗口组

property

属性部分和小窗口组一样。

events

evExtend是放大按钮对应的事件,evDown为向下按钮,evBack为返回按钮,evSwapFrame为交换事件。

然后将组件添加到图像对象中

state

记得将按钮的位置调整一下。

1

此部分为显示窗口组件,组件功能之前提过这里就不说了。

2

1、根据本界面的父界面传递的LayoutStatus参数设置本界面上的按钮的ID参数

2、根据LayoutStatus参数的mode和module字段设置当前应该显示的子界面。

3

设置按钮事件对应的处理操作。

evBack

evback

当点击返回按钮时,发送返回事件给本窗口的父窗口,同时将向下和扩展按钮显示出来。

evDown

evDown

点击向下按钮时,发送向下事件给本窗口的父窗口,同时将返回按钮显示出来,向下和扩展按钮隐藏,因为界面尺寸变了,返回按钮的位置也调整一下。

evExtend

evextend

点击扩展按钮时,发送控制事件给父窗口,同时将返回按钮显示,向下和扩展按钮隐藏。

4

Normal->Drag

normal2drag

保存当前鼠标坐标

Drag

drag

实现鼠标移动时,鼠标拖拽的界面也一起移动。

Drag->Normal

鼠标松开后判断是否需要交互界面

drag2normal

至此窗口组部分结束

布局

创建一个Layout的子文件夹。在子文件夹中创建一个Layout的图像对象

layout

然后将上面的大小组件按照布局要求添加进来

component

默认位置是叠在一起的。

接下来设置布局的一些设置

state

1

将最开始导入工程的LayoutStatusArrayData添加到Layout中,并设置其数据。

2

用LayoutStatusArrayData设置所有的子界面。设置完成后就会看到视频中的布局了。

dataflow

3

捕获子界面的按钮事件并处理

扩展按钮

按钮按下后显示扩展型界面。

extend

(按钮的显示与隐藏在各自的界面中操作,布局界面不涉及)

下拉按钮

下拉按钮对应的大界面的位置偏移原点与原始的不同,需要修改一下,同样的,返回按钮操作时需要将偏移原点改回去。

donw

返回按钮

不论是从哪一个状态返回,最终都是返回到Main窗口的中尺寸。这样的话,处理就简单一点。

首先将mode置为2用于显示中窗口,然后将操作的界面的位置偏移原点设置为布局最开始的状态。

evback

交换界面

先查找旧界面的模块值,保存下来。然后查找新界面的模块值并赋给旧模块。最后将保存的模块值赋值给新模块。(这个就是简单的数组排序算法的应用)

swap

至此所有的开发结束

运行

创建一个子文件夹,名称为Apps。在此文件夹内添加一个Format,名称为MouseMultiFrame。将上面的layout添加到里面。

app

右键选中MouseMultiFrame生成代码,编译运行。

按照视频操作就可以了(放大,返回,下拉、拖拽交换功能)。

结果

完整工程下载:MouseMultiFrame