Fltk 1.3 系列教程(3)
深入布局
這節課我們學習如何把Fltk的各種Widgets排列在一起,對控件大小變化做出正確的反應。這里主要學習兩個類:Fl_Widget和Fl_Group,他們是繼承關系,前者是所有控件的父類,不能創建(只能繼承),后者是所有容器的父類,可以作為通用容器創建。
上節課學的靜態布局是布局的基礎,但是不能對容器大小的改變做出正確的反應。使用Fl_Group把各種控件邏輯上組織起來,然后使用resizable函數,可以實現較為高級的布局。
1. 位置與大小
首先我們講Fl_Widget有三個改變大小和位置的函數,分別如下:
size(width, height)
position(x, y)
resize(x, y, width, height)
這三個函數分別改變一個控件的大小、位置、同時大小和位置,比較簡單,就不多說了。記著調用這些函數后可能需要調用redraw()重畫控件。
2. 父與子
重點在于Fl_Group,這個類包含了一個子控件的列表,可以通過一系列方法來控制這個列表,例如:
Fl_Widget *array() //獲得這個列表(數組)
int children() //獲得列表的大?。ㄓ袔讉€子控件)
Fl_Widget *child(int n) //獲得第n個子控件
int find(Fl_Widget *) //查找某子控件在列表中的位置
clear() //清空整個列表,刪除所有子控件
add(Fl_Widget *) // 把控件添加到列表尾
insert(Fl_Widget *, int index) or insert(Fl_Widget* , Fl_Widget * before) // 把控件添加到某個位置或控件之后
remove(Fl_Widget *) or remove(int index) // 刪除某個控件
//注意添加操作會把控件從當前的父控件的列表刪除,添加到另一個父控件的列表
// 刪除操作并不會在內存刪除控件,只是從列表中移除
相應的Fl_Widget有個方法叫parent()可以找到其父容器。因此,如果在一開始沒有使用begin和end正確添加子控件,使用這些方法也可以比較方便地添加、刪除、管理子控件。
3. 重頭戲:resizable(Fl_Widget *)
這是Fl_Group的一個方法,在上一節課中我們也看到了,把參數設為容器自己使得自己可以改變大小,而且所有的子控件會在改變大小的時候隨之縮放。但是更常用的是把某一個子控件設為resizable,這樣不僅容器可以改變大小,也會使得相應的子控件有較好的行為。下面這個圖摘自Fltk 1.3 Manual,顯示了resizable的行為:


大家可以看到,在resizable的橫向范圍和縱向范圍內的控件都改變了大小和位置,而在四個角位置的控件沒有改變大小。在橫向范圍內的控件只改變高度,在縱向范圍內的控件只改變寬度。這樣,只要使用Fl_Group和不可見的Fl_Box,就可以完成各種復雜的縮放行為。例如下面的對話框:


記住Fltk的文本輸入框前面的文字標簽是和輸入框綁定在一起的,不用另外使用標簽控件。而輸入框的左上角坐標卻不包含標簽,也就是說左上角是輸入框的左上角,而不是加上標簽后的左上角。這在指定坐標的時候要注意。
上面的程序,“姓名”一行包含在一個Group中,這個Group的輸入框是它的resizable,從而“檢查”按鈕在輸入框的橫向范圍內,只能改變高度,但是整個Group在Multiline_Input的縱向范圍內,所以只能改變寬度,于是Group內的Input可以改變寬度,而“檢查”按鈕大小就不能變化了?!班]件”行的實現同上。最下面的“好的”按鈕,實現和上面“姓名”和“郵件”一樣,只不過把Input換成了一個不可見的Fl_Box。整個源程序如下(layout.cc):
#include "FL/Fl.H"
#include "FL/Fl_Double_Window.H"
#include "FL/Fl_Group.H"
#include "FL/Fl_Button.H"
#include "FL/Fl_Input.H"
#include "FL/Fl_Multiline_Input.H"
#include "FL/Fl_Box.H"
int main(int argc, char **argv)
{
Fl_Double_Window *w = new Fl_Double_Window(100, 200, 460, 320, "Fltk布局");
//size_range只可以用在頂層窗口,設置窗口resize的范圍。它有7個參數,但只有前4個
//是在各個系統下都有效的。前兩個是最小寬度和最小高度。后兩個是最大寬度和
//最大高度。后兩個設置成零,說明無限制。如果設置成和前面最小值一樣,則不可變動
//大小。例如
//w->size_range(w->w(), w->h(), 0, w->h());
//設置窗口高度不變,寬度可變。
w->size_range(w->w(), w->h(), 0, 0);
Fl_Group * group1 = new Fl_Group(10, 10, w->w()-20, 30);
Fl_Input *input1 = new Fl_Input(50, 10, w->w()-175, 30, "姓名:");
Fl_Button *b1 = new Fl_Button(w->w()-110, 10, 100, 30, "檢查!");
group1->end();
group1->resizable(input1);
Fl_Group * group2 = new Fl_Group(10, 50, w->w()-20, 30);
Fl_Input *input2 = new Fl_Input(50, 50, w->w()-175, 30, "郵件:");
Fl_Button *b2 = new Fl_Button(w->w()-110, 50, 100, 30, "檢查!");
group2->end();
group2->resizable(input2);
Fl_Multiline_Input * comments = new Fl_Multiline_Input(50, 100, w->w()-60, w->h()-150, "評論:");
Fl_Group *group3 = new Fl_Group(10, w->h()-10-30, w->w()-20, 30);
Fl_Box *b = new Fl_Box(10, w->h()-10-30, group3->w() - 100, 30); //Fl_Box是默認不可見的
Fl_Button *b3 = new Fl_Button(w->w()-10-100, w->h()-10-30, 100, 30, "好的");
group3->end();
group3->resizable(b);
w->end();
w->resizable(comments);
w->show();
return Fl::run();
}
4. 使用Fl_Pack
Fl_Pack是Fl_Group的子類型,實現了額外的一些布局功能。Fl_Pack能自動把它的子控件按橫向或縱向排列(通過方法type(Fl_Pack::HORIZONTAL或Fl_Pack::VERTICAL)來指定),這樣子控件就不用再考慮位置(隨便指定位置就行),但是還要指定正確的大小。這就是Fl_Pack和Fl_Group的不同之處。例如上述程序中使用Group的代碼,可以用以下代碼替換
Fl_Pack
*
pack1
=
new Fl_Pack(50, 10,
w->w()-20, 30);
pack1->type(Fl_Pack::HORIZONTAL);
pack1->spacing(10);
Fl_Input
*input1
=
new Fl_Input(0,0,w->w()-175, 30, "姓名:"); //
注意位置設為0, 0,沒有影響
Fl_Button
*b1
=
new Fl_Button(0,0,100, 30, "檢查!");
pack1->end();
pack1->resizable(input1);
可以看到,Fl_Pack有個spacing方法,指定了子控件之間的距離,而且子控件不用再考慮位置,因為所有的位置將由Fl_Pack來自動指定。Fl_Pack對手工布局有所幫助,但如果使用Fluid設計器,幫助不大。
5. 總結和下次預告
在fltk中實現正確的resize行為,需要使用Fl_Group的resizable方法。所有控件的坐標相對于整個頂層窗口(在今后的版本中可能改變)。Fl_Pack可以省去指定位置的麻煩,添加了一些方便性。有關布局的控件還有好幾個,例如實現標簽頁的Fl_Tabs,實現拆分窗口的Fl_Tile,實現滾動顯示的Fl_Scroll等。這些控件都很簡單,將在今后的課程中做專門介紹。
下次我們將不再討論布局的問題,主要講一下怎樣處理事件,這樣我們的界面會對用戶的輸入做出反應,就能實現一些比較簡單的有用的應用了。
參考
1. http://www.fltk.org/articles.php?L415 關于resizable怎樣工作的好文章
2. Fltk 1.3 Manual

浙公網安備 33010602011771號