SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 4317|回复: 0
打印 上一主题 下一主题

[gui2] mtwusc控件

[复制链接]

187

主题

346

帖子

2450

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2450
跳转到指定楼层
楼主
发表于 2017-3-30 19:51:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ancientcc 于 2020-11-29 21:20 编辑

mtwusc是calculate Maximum Text Width Using Same Column control的缩写,指通过同列控件计算最大正文宽度。《Rose编程指南》的“5.2 网式布局”已大致说了这种控件。这里补充深入代码的细节。引入mtwusc的目的是要处理多行文本。

mtwusc控件分为两类。一是脚本中含有mtwusc=yes,可能的控件有label、button。二是scroll_text,内中的text_box强制是mtwusc。但不论哪一种,到twidget这一层,指的是twidget.mtwusc_会被设为true。

tcontrol中有个int类型的text_maximum_width_字段,只有在mtwusc时,它才可能是非0。另外,mtwusc控件中的tcontrol.config_->label_is_text一定是true。

calculate_best_size_bh
为什么要新加calculate_best_size_bh函数?calculate_best_size_bh要从最顶层的窗口传播向最底层的单元控件,这意味着多种控件要重载它。但为降低工作量,还是会问能不能不加这函数?办法是把它的功能加入到已有的calculate_best_size。这造成的结果是每次调用calculate_best_size都会执行calculate_best_size_bh逻辑,而并不是所有calculate_best_size都需要那功能,这会增加cpu负荷。

calculate_best_size_bh的返回值指示该控件在正确计算了内中mtwusc控件后的标称尺寸。如果该控件可以明确内部没有mtwusc,可直接返回(twidget::npos, twidget::npos)。

calculate_best_size_bh有两个作用,一是设置mtwusc的容许宽度(text_maximum_width_),二是计算包含了mtwusc的容器控件的标称尺寸。考率到效率,除了mtwusc,其它控件的尺寸都使用之前留下的col_width_、row_height_,mtwusc则是用此次算出的覆盖之前的col_width_、row_height_。所以之前的col_width_、row_height_要具备两个条件。一是calculate_best_size后的直接值 ,二是对mtwusc,要能固定返回(0, 0),即保证条件text_maximum_width_==0。
  • calculate_best_size的后的直接值。在calculate_best_size_bh前需强制调用一次get_best_size。(mtwusc不可能是等尺寸组中控件,因而它的get_best_size一定会触发去调用calculate_best_size)。
  • 对mtwusc,要固定返回(0, 0)。考虑到复杂度,此时很难保证“text_maximum_width_=0”,为此增加一个叫clear_restrict_width_cell_size的变量,它是true时,calculate_best_size计算restrict_width_控件时固定返回(0, 0)。为方便管理clear_restrict_width_cell_size,增加了tclear_restrict_width_cell_size_lock类。

由于以上两个原因,calculate_best_size_bh相关代码往往是以下样子。执行得到的size包含了mtwusc的标称尺寸。
  1. {
  2.         tclear_restrict_width_cell_size_lock lock;
  3.         panel.get_best_size();
  4. }
  5. tpoint size = panel.calculate_best_size_bh(content_->get_width());
复制代码

  1. tpoint tcontrol::calculate_best_size_bh(const int width)
  2. {
  3.         if (!mtwusc_) {
  4.                 return tpoint(twidget::npos, twidget::npos);
  5.         }
  6.         set_text_maximum_width(width);
  7.         tpoint size = get_best_size();
  8.         if (size.x > config_->text_extra_width) {
  9.                 size.x = width;
  10.         } else {
  11.                 VALIDATE(size.x == config_->text_extra_width, null_str);
  12.                 VALIDATE(label_.empty(), null_str);
  13.         }
  14.         return size;
  15. }
复制代码
calculate_best_size_bh是mtwusc_控件计算标称尺寸的通用函数。依次执行两个操作,1)设置mtwusc_的容许宽度(text_maximum_width_),2)计算该控件的标称尺寸。对如何计算尺寸,可以发现就两个值,或是参数传下的width,或是text_extra_width。当text_extra_width时,意味着label_字段是空。为什么label_是空时要让算出的标称宽度是text_extra_width?一个没有内容的控件,还是让按惯性认为的返回0。这里要注意,传下的width是考虑了放大系数的,由于calculate_best_size_bh会返回两种值,有这么个结论,label_非空时返回的标称尺寸考虑了放大系数,空时则没有。

放置(palce)
放置阶段要额外处理mtwusc的原因是mtwusc控件在计算标称尺寸时已包含了放大系数!这就使得tgrid::place在水平方向拉伸时不应该再位伸这控件了,否则会导致根据放大系数计算(重复计算了)出的渲染尺寸变大。由于渲染宽度变大,导致需要的高度变小,底部出现空白。

放置涉及到函数就一个,tgrid::place。
  1. void tgrid::place(const tpoint& origin, const tpoint& size) {
  2.         ......
  3.         tpoint best_size = calculate_best_size();

  4.         for (int at = 0; at < children_vsize_; at ++) {
  5.                 if (children_[at].widget_->get_visible() == twidget::INVISIBLE) {
  6.                         continue;
  7.                 }
  8.                 if (children_[at].widget_->mtwusc() && col_width_[at % cols_]) {
  9.                         VALIDATE(restrict_width_factor.first == twidget::npos || (restrict_width_factor.first % cols_ == at % cols_), null_str);

  10.                         tcontrol* widget = dynamic_cast<tcontrol*>(children_[at].widget_);
  11.                         if (!widget->label().empty()) {
  12.                                 const tchild& cell = child(at);
  13.                                 const int new_best_width = widget->get_multiline_best_width() + cell_border_space(cell).x;
  14.                                 VALIDATE(new_best_width >= col_width_[at % cols_], null_str);
  15.                                 best_size.x += new_best_width - col_width_[at % cols_];
  16.                                 col_width_[at % cols_] = new_best_width;
  17.                                 restrict_width_factor = std::make_pair(at, col_grow_factor_[at % cols_]);
  18.                         }
  19.                 }
  20.         }
  21.         ......
  22. }
复制代码
以上代码的目的是把不须要再拉伸的控件放入restrict_width_factor,当前满足的只有mtwusc控件。但不是只要mtwusc就放入,calculate_best_size_bh有说,label_是空时mtwusc的标称尺寸不包含放大系数,这时还放入的话会导致错误。

tip是个mtwusc控件,当tip中label_是空时,标称尺寸不包含放大系数。这意味着它所在列的标称宽度将由同列的anim_grid、list决定。假设list是空,anim_grid决定出是38像素。这时还把tip归入restrict_width_factor,会导致tgrid::place不再拉伸anim_grid和list_grid,它们的渲染宽度就固定在32像素!

col_width_[at % cols_] != 0是判断该mtwusc控件的label_是否空的初始条件,加它是为了减少进入if的次数。label_空时col_width_依旧!=0,那可能是两个原因。1)该控件可能有text_extra_width,2)该列可能存在其它控件,那些控件会使col_width_不是0。

“if (!widget->label().empty())”使得只有计算了放大系数的mtwusc控件才会被放入restrict_width_factor。

“const int new_best_width = widget->get_multiline_best_width() + cell_border_space(cell).x”。col_width_存储的是考虑了board后的值,所以计算new_best_width要加上cell_border_space。

“(VALIDATE(new_best_width >= col_width_[at % cols_], null_str)”。为什么new_best_width可能大于col_width_[at % cols_]?new_best_width用的是get_multiline_best_width()(值等于进入tgrid::place前的col_width_),col_width_用的是进入tgrid::place用get_best_size新算出的尺寸。举个例子,上图中gender_grid中的nation是mtwusc控件,它的text_maximum_width_是76,get_best_size以76作为容许宽度调用get_best_text_size,算出的是72。就造成new_best_width比col_width_[at % cols_]多4个像素。也就是说,要计算label_不是空的mtwusc控件第二阶段的标称尺寸,不能用get_best_text_size,而是须要用get_multiline_best_width。

  1. best_size.x += new_best_width - col_width_[at % cols_];
  2. col_width_[at % cols_] = new_best_width;
复制代码

为什么要修改“best_size.x、col_width_[at % cols_]?——在调用此个tgrid::place的前面会调用过calculate_best_size_bh,它的确会把col_width_设到正确width,但进入此个tgrid::place,首先会调用calculate_best_size,这函数会重新计算best_size、col_width_。这两个修改的作用就是恢复“at % cols”这一列的“原”渲染尺寸(已经计算了放大系数)。tcontrol::get_multiline_best_width和tcontrol::set_text_maximum_width是一对原、反函数,一个由width算出text_maximum_width_,另一个由text_maximum_width_算出width,它们只是几个减法或加法,计算较快。
  1. int tcontrol::calculate_maximum_text_width(const int maximum_width) const
  2. {
  3.         int ret = maximum_width - config_->text_extra_width;
  4.         if (config_->label_is_text) {
  5.                 ret -= config_->text_space_width;
  6.         }
  7.         return ret >=0 ? ret: 0;
  8. }

  9. // (1/2)width ==> text_maximum_width_
  10. void tcontrol::set_text_maximum_width(int maximum)
  11. {
  12.         if (mtwusc_) {
  13.                 text_maximum_width_ = calculate_maximum_text_width(maximum);
  14.         }
  15. }

  16. // (2/2)text_maximum_width_ ==> width
  17. int tcontrol::get_multiline_best_width() const
  18. {
  19.         VALIDATE(mtwusc_, null_str);
  20.         int ret = text_maximum_width_ + config_->text_extra_width;
  21.         if (config_->label_is_text) {
  22.                 ret += config_->text_space_width;
  23.         }
  24.         return ret;
  25. }
复制代码

为加深理解为什么要修改,让回看tip示例。get_best_size算出的col_width_只是tip中字符串长度,而不是calculate_best_size_bh传下的width!这时如果tip字符串不长,甚至填不满一行,那这个col_width_极可能远小于width。即使tip是多行,col_width_也可能比width少几个像素。于是既然该列此刻不再拉伸了,那它的col_width_就该正确反应拉伸结果,即恢复到之前的width,get_multiline_best_width就用于计算这个width。

不支持mtwusc的控件
除了tscroll_panel外的那些从tscroll_container派生的类。包括scroll_label、scroll_text、listbox、report、tree。为统一,tscrooll_container重载calculate_best_size_bh,直接返回tpoint(npos, npos),然后tscroll_panel再重载,实现自个的操作逻辑。
  1. tpoint tscroll_container::calculate_best_size_bh(const int width)
  2. {
  3.         return tpoint(twidget::npos, twidget::npos);       
  4. }
  5. tpoint tscroll_panel::calculate_best_size_bh(const int width)
  6. {
  7.         ......
  8.         tpoint text_size = content_grid_->calculate_best_size_bh(width);
  9.         ......
  10. }
复制代码

为什么tscroll_panel::calculate_best_size_bh不能直接返回content_grid_->calculate_best_size_bh?窗口配置文件可能给这tscroll_panel写了width、height字段,为此要用和tcontrol::get_best_size差不多的逻辑综合两种尺寸。

以下控件需正确实现calculate_best_size_bh。
label
tcontainer_它同时实现了panel, toggle_panel, window
stack适用于的配置尺寸。用在选择控件
scroll_panel
grid
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2024-5-20 00:48 , Processed in 0.046379 second(s), 22 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表