GacUI在結(jié)束了文本框的介紹之后,開(kāi)始進(jìn)入列表的介紹。列表內(nèi)容豐富,包含各種預(yù)定義的列表控件、用來(lái)顯示和操作大量對(duì)象的虛擬模式、MVC分離、修改列表樣式等內(nèi)容。今天先從文本列表的簡(jiǎn)單操作開(kāi)始。這個(gè)Demo展示了如何對(duì)列表進(jìn)行添加和刪除。窗口里面有一個(gè)列表,然后有添加和刪除兩個(gè)按鈕,分別用于把文本框的內(nèi)容添加到列表內(nèi),和刪除掉選中的列表項(xiàng)的。在這個(gè)Demo里面只允許列表項(xiàng)單選,并且水平滾動(dòng)條默認(rèn)不出現(xiàn)。先看圖:

空間如何布局,我就不再贅述了,明顯是一個(gè)四行三列的表格。代碼如下:
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
// for SortedList, CopyFrom and Select
using namespace vl::collections;
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
class NameEditorWindow : public GuiWindow
{
private:
GuiTextList* listBox;
GuiSinglelineTextBox* textBox;
GuiButton* buttonAdd;
GuiButton* buttonRemove;
void buttonAdd_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// add the specified name at the end of the list box
listBox->GetItems().Add(textBox->GetText());
textBox->SelectAll();
textBox->SetFocus();
}
void buttonRemove_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// remove the selected items using item index
listBox->GetItems().RemoveAt(listBox->GetSelectedItems()[0]);
}
void listBox_SelectionChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// disable the button if no item is selected
buttonRemove->SetEnabled(listBox->GetSelectedItems().Count()>0);
}
public:
NameEditorWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.ListBox.NameEditor");
GuiTableComposition* table=new GuiTableComposition;
table->SetRowsAndColumns(4, 3);
table->SetCellPadding(3);
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::MinSizeOption());
table->SetRowOption(2, GuiCellOption::MinSizeOption());
table->SetRowOption(3, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(1, GuiCellOption::MinSizeOption());
table->SetColumnOption(2, GuiCellOption::MinSizeOption());
this->GetContainerComposition()->AddChild(table);
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 4, 1);
listBox=g::NewTextList();
listBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
listBox->SetHorizontalAlwaysVisible(false);
listBox->SelectionChanged.AttachMethod(this, &NameEditorWindow::listBox_SelectionChanged);
cell->AddChild(listBox->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 1, 1, 1);
GuiLabel* label=g::NewLabel();
label->SetText(L"Name to add: ");
label->GetBoundsComposition()->SetAlignmentToParent(Margin(0, -1, 0, 0));
cell->AddChild(label->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 2, 1, 1);
textBox=g::NewTextBox();
textBox->GetBoundsComposition()->SetPreferredMinSize(Size(120, 23));
textBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
cell->AddChild(textBox->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 1, 1, 2);
buttonAdd=g::NewButton();
buttonAdd->SetText(L"Add");
buttonAdd->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonAdd->Clicked.AttachMethod(this, &NameEditorWindow::buttonAdd_Clicked);
cell->AddChild(buttonAdd->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(2, 1, 1, 2);
buttonRemove=g::NewButton();
buttonRemove->SetText(L"Delete");
buttonRemove->SetEnabled(false);
buttonRemove->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
buttonRemove->Clicked.AttachMethod(this, &NameEditorWindow::buttonRemove_Clicked);
cell->AddChild(buttonRemove->GetBoundsComposition());
}
// set the preferred minimum client size
this->GetBoundsComposition()->SetPreferredMinSize(Size(480, 480));
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()
this->ForceCalculateSizeImmediately();
// move to the screen center
this->MoveToScreenCenter();
}
};
void GuiMain()
{
GuiWindow* window=new NameEditorWindow;
GetApplication()->Run(window);
delete window;
}
這里需要注意的幾點(diǎn)就是,為了實(shí)現(xiàn)在列表沒(méi)有選中內(nèi)容的時(shí)候禁用刪除按鈕,我們需要監(jiān)聽(tīng)GuiTextList::SelectionChanged事件。核心的代碼就是下面這幾行:
void buttonAdd_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// add the specified name at the end of the list box
listBox->GetItems().Add(textBox->GetText());
textBox->SelectAll();
textBox->SetFocus();
}
void buttonRemove_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// remove the selected items using item index
listBox->GetItems().RemoveAt(listBox->GetSelectedItems()[0]);
}
void listBox_SelectionChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// disable the button if no item is selected
buttonRemove->SetEnabled(listBox->GetSelectedItems().Count()>0);
}
GuiTextList控件的GetItems函數(shù)返回所有的列表項(xiàng)。這個(gè)對(duì)象有Add、Insert、Clear、IndexOf、Remove、RemoveAt、Contains、Count等函數(shù),可以用來(lái)操作列表項(xiàng)。GuiTextList還有GetSelectedItems函數(shù)(其實(shí)是定義在GuiSelectableListControl里面的),可以用來(lái)獲得所有選中的列表項(xiàng)的下標(biāo)(從0開(kāi)始)。每當(dāng)列表內(nèi)容被修改的時(shí)候,GetSelectedItems的結(jié)果就會(huì)被自動(dòng)清空。
下一個(gè)Demo將是關(guān)于如何處理允許多選的列表的操作方法。
posted @
2012-05-23 04:42 陳梓瀚(vczh) 閱讀(2360) |
評(píng)論 (4) |
編輯 收藏
這是
GacUI關(guān)于文本框高亮的最后一個(gè)Demo了。這個(gè)Demo是關(guān)于XML著色的。XML著色比起C++著色更難,主要是因?yàn)樵陬愃?lt;book name="C++ Primer">這樣的代碼里面,book和name的顏色不一樣,<和book的顏色也不一樣(參考Visual Studio)。這種時(shí)候單純依靠正則表達(dá)式來(lái)區(qū)分顏色是不夠的,我們還需要引入一個(gè)新的狀態(tài)機(jī)。這個(gè)狀態(tài)機(jī)只有三個(gè)狀態(tài),用來(lái)區(qū)分tag name,attribute name和text三種顏色。狀態(tài)機(jī)是手寫(xiě)的,并且GacUI提供了一個(gè)回調(diào)來(lái)寫(xiě)這個(gè)只有寥寥幾行狀態(tài)機(jī)。先看圖:

跟C++一樣,XML著色首先是由正則表達(dá)式組成的。XML的正則表達(dá)式比較簡(jiǎn)單,只有符號(hào)、name、字符串、CData、注釋和其它一些簡(jiǎn)單的東西:
1 class XmlColorizer : public GuiTextBoxRegexColorizer
2 {
3 public:
4 XmlColorizer()
5 {
6 text::ColorEntry entry=win7::Win7GetTextBoxTextColor();
7 SetDefaultColor(entry);
8
9 entry.normal.text=Color(0, 128, 0);
10 AddToken(L"/<!--([^/-]|-[^/-]|--[^>])*--/>", entry);
11
12 entry.normal.text=Color(128, 0, 255);
13 AddToken(L"/<!/[CDATA/[([^/]]|/][^/]]|/]/][^>])*/]/]/>", entry);
14
15 entry.normal.text=Color(0, 0, 0);
16 AddToken(L"\"[^\"]*\"", entry);
17
18 entry.normal.text=Color(0, 0, 255);
19 AddToken(L"[<>=]", entry);
20
21 entry.normal.text=Color(255, 0, 0);
22 AddToken(L"[a-zA-Z0-9_/-:]+", entry);
23
24 entry.normal.text=Color(163, 21, 21);
25 AddExtraToken(entry);
26
27 Setup();
28 }
29
30 void ColorizeTokenContextSensitive(const wchar_t* text, vint start, vint length, vint& token, int& contextState)override
31 {
32 
33 }
34
35 int GetContextStartState()override
36 {
37 
38 }
39 }; 其次要對(duì)三種地方的[a-zA-Z0-9_/-:]進(jìn)行著色。Tag的名字用褐色,attribute的名字用紅色,而普通文本用黑色。因此我們可以做一個(gè)狀態(tài)機(jī),初始狀態(tài)為0。如果讀進(jìn)了<,狀態(tài)變成1。1遇到了一個(gè)Tag名字之后變?yōu)?。從2開(kāi)始所有的名字就只能是attribute的名字了。我們只考慮正確的情況,錯(cuò)誤的代碼著色出了問(wèn)題不僅沒(méi)有壞處,還可以提醒程序員有什么地方寫(xiě)錯(cuò)了。之后遇到了>變回0,在0的狀態(tài)下所有的東西都是普通文本,所以名字就都是黑色的。因此上面ColorizeTokenContextSensitive函數(shù)中就需要填入這個(gè)邏輯。GetContextStartState返回0,作為第一行的起始狀態(tài)。代碼如下:
void ColorizeTokenContextSensitive(const wchar_t* text, vint start, vint length, vint& token, int& contextState)override
{
// 0 < 1 name 2 att > 0
switch(token)
{
case 3:
if(length==1)
{
switch(text[start])
{
case '<':
contextState=1;
break;
case '>':
contextState=0;
break;
}
}
break;
case 4:
switch(contextState)
{
case 0:
token=-1;
break;
case 1:
token=5;
contextState=2;
break;
}
break;
}
}
int GetContextStartState()override
{
return 0;
}
這個(gè)函數(shù)里面有幾個(gè)魔法數(shù)字,其實(shí)都是關(guān)于Token的編號(hào)的。構(gòu)造函數(shù)里面我們使用AddToken將一個(gè)顏色關(guān)聯(lián)到正則表達(dá)式上,使用AddExtraToken創(chuàng)建一個(gè)沒(méi)有正則表達(dá)式關(guān)聯(lián)的顏色。所以在這個(gè)狀態(tài)機(jī)里面,所有的顏色都用Token的序號(hào)來(lái)表示。無(wú)論是使用AddToken還是AddExtraToken,第一個(gè)顏色編號(hào)為0,第二個(gè)顏色編號(hào)為1。因此case 3指的是[<>=],而case 4指的是[a-zA-Z0-9_/-:]+。而case 4里面的token=5則表示在狀態(tài)為1的時(shí)候,名字都用AddExtraToken指定的那個(gè)褐色進(jìn)行染色。缺省的名字(也就是id為4的token)是紅色,所以不需要對(duì)contextState為2的時(shí)候進(jìn)行處理。
這樣我們就完成了XML的著色。GacUI接下來(lái)的幾個(gè)Demo將會(huì)是關(guān)于ListBox、ListView和TreeView的,敬請(qǐng)期待。
posted @
2012-05-20 00:41 陳梓瀚(vczh) 閱讀(2246) |
評(píng)論 (2) |
編輯 收藏
GacUI終于把
上一篇文章中提到的自動(dòng)采用正則表達(dá)式進(jìn)行高亮的Demo做出來(lái)了。這次實(shí)現(xiàn)的是C++ colorizer。而XML colorizer不僅需要正則表達(dá)式,還需要一個(gè)人工維護(hù)的狀態(tài),這個(gè)等到下一個(gè)Demo再提及。先看圖

在不需要人工維護(hù)狀態(tài),僅通過(guò)正則表達(dá)式就可以著色的時(shí)候,編寫(xiě)一個(gè)colorizer變得十分的簡(jiǎn)單。這個(gè)Colorizer雖然不是一定非得通過(guò)繼承來(lái)實(shí)現(xiàn),但是這個(gè)Demo還是使用了繼承。首先編寫(xiě)一個(gè)類,繼承自GuiTextBoxRegexColorizer,然后在構(gòu)造函數(shù)里面填寫(xiě)下面的代碼:
class CppColorizer : public GuiTextBoxRegexColorizer
{
public:
CppColorizer()
{
text::ColorEntry entry=win7::Win7GetTextBoxTextColor();
SetDefaultColor(entry);
entry.normal.text=Color(128, 0, 255);
AddToken(L"/d+(./d*)?([eE][+/-]?/d+)?", entry);
entry.normal.text=Color(163, 21, 21);
AddToken(L"\"([^\"]|\\\\/.)*\"", entry);
entry.normal.text=Color(0, 128, 0);
AddToken(L"////[^\r\n]*", entry);
AddToken(L"///*(//|[*]*[^*//])*/*+//", entry);
entry.normal.text=Color(0, 0, 255);
AddToken(L"#[a-zA-Z0-9_]*", entry);
AddToken(CppKeywords, entry);
AddToken(L"[a-zA-Z0-9_]+", GetDefaultColor());
Setup();
}
};
然后只需要把它綁定到文本框里面就可以了。在這個(gè)Demo里面,我們?cè)谙吕虻氖录锩嫣砑酉旅娴拇a:
void comboSelector_SelectedIndexChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
switch(comboSelector->GetSelectedIndex())
{
case 0:(略)
case 1:(略)
case 2:
textBox->SetColorizer(new CppColorizer);
textBox->SetText(
L"#include <iostream>\r\n"
L"using namespace std;\r\n"
L"\r\n"
L"int main()\r\n"
L"{\r\n"
L"\t//This is a comment\r\n"
L"\t/**This*is/another\r\n"
L"\tcomment**/\r\n"
L"\tcout<<\"Hello, world!\"<<endl;\r\n"
L"\treturn 0;\r\n"
L"}\r\n"
);
break;
default:
textBox->SetColorizer(0);
}
}
當(dāng)然這樣是不完整的,因?yàn)镃ppColorizer里面還有一個(gè)CppKeywords的常量。這實(shí)際上是一個(gè)正則表達(dá)式,里面用“|”字符把所有C++的關(guān)鍵字連了起來(lái)。內(nèi)容抄自MSDN的C++ Language Reference:
1 const wchar_t* CppKeywords=
2 L"__abstract|"
3 L"__alignof|"
4 L"__asm|"
5 L"__assume|"
6 L"__based|"
7 L"__box|"
8 L"__cdecl|"
9 L"__declspec|"
10 L"__delegate|"
11 L"__event|"
12 L"__except|"
13 L"__fastcall|"
14 L"__finally|"
15 L"__forceinline|"
16 L"__gc|"
17 L"__hook|"
18 L"__identifier|"
19 L"__if_exists|"
20 L"__if_not_exists|"
21 L"__inline|"
22 L"__int16|"
23 L"__int32|"
24 L"__int64|"
25 L"__int8|"
26 L"__interface|"
27 L"__leave|"
28 L"__m128d|"
29 L"__m128|"
30 L"__m128i|"
31 L"__m64|"
32 L"__multiple_inheritance|"
33 L"__nogc|"
34 L"__noop|"
35 L"__pin|"
36 L"__property|"
37 L"__raise|"
38 L"__sealed|"
39 L"__single_inheritance|"
40 L"__stdcall|"
41 L"__super|"
42 L"__try|"
43 L"__except|"
44 L"__finally|"
45 L"__try_cast|"
46 L"__unaligned|"
47 L"__unhook|"
48 L"__uuidof|"
49 L"__value|"
50 L"__virtual_inheritance|"
51 L"__w64|"
52 L"__wchar_t|"
53 L"wchar_t|"
54 L"abstract|"
55 L"array|"
56 L"bool|"
57 L"break|"
58 L"case|"
59 L"catch|"
60 L"char|"
61 L"class|"
62 L"const_cast|"
63 L"const|"
64 L"continue|"
65 L"decltype|"
66 L"default|"
67 L"delegate|"
68 L"delete|"
69 L"do|"
70 L"double|"
71 L"dynamic_cast|"
72 L"else|"
73 L"enum|"
74 L"event|"
75 L"explicit|"
76 L"extern|"
77 L"false|"
78 L"finally|"
79 L"float|"
80 L"for|"
81 L"friend|"
82 L"gcnew|"
83 L"generic|"
84 L"goto|"
85 L"if|"
86 L"initonly|"
87 L"inline|"
88 L"int|"
89 L"interface|"
90 L"interior_ptr|"
91 L"literal|"
92 L"long|"
93 L"mutable|"
94 L"namespace|"
95 L"new|"
96 L"new|"
97 L"nullptr|"
98 L"operator|"
99 L"private|"
100 L"property|"
101 L"property|"
102 L"protected|"
103 L"public|"
104 L"register|"
105 L"reinterpret_cast|"
106 L"return|"
107 L"sealed|"
108 L"short|"
109 L"signed|"
110 L"sizeof|"
111 L"static_assert|"
112 L"static_cast|"
113 L"static|"
114 L"struct|"
115 L"switch|"
116 L"template|"
117 L"this|"
118 L"__thiscall|"
119 L"throw|"
120 L"true|"
121 L"try|"
122 L"typedef|"
123 L"typeid|"
124 L"typeid|"
125 L"typename|"
126 L"union|"
127 L"unsigned|"
128 L"using|"
129 L"virtual|"
130 L"void|"
131 L"volatile|"
132 L"while";
使用GacUI為文本框著色已經(jīng)變得如此簡(jiǎn)單。
posted @
2012-05-17 09:03 陳梓瀚(vczh) 閱讀(2199) |
評(píng)論 (2) |
編輯 收藏
摘要: GacUI的高亮Demo做了一半。現(xiàn)在的進(jìn)度是,可以手寫(xiě)著色器的狀態(tài)轉(zhuǎn)換函數(shù),但是自動(dòng)從正則表達(dá)式產(chǎn)生著色器的狀態(tài)轉(zhuǎn)換函數(shù)部分還沒(méi)有集成進(jìn)GacUI。這篇博客還是照舊,看圖、看Demo代碼,說(shuō)著色原理。 這次的Demo要做一個(gè)可以動(dòng)態(tài)切換著色器的小程序,里面包含INI、XML和C++三種著色器。現(xiàn)在只實(shí)現(xiàn)了INI一中,手寫(xiě)的...
閱讀全文
posted @
2012-05-11 08:58 陳梓瀚(vczh) 閱讀(3255) |
評(píng)論 (1) |
編輯 收藏
摘要: GacUI添加了一個(gè)新的Demo。這個(gè)Demo用了幾個(gè)按鈕(之所以不用菜單是因?yàn)椴幌胱孌emo一下子包含太多新東西)來(lái)實(shí)現(xiàn)剪貼板操作、只讀控制和行跳轉(zhuǎn)等功能。在剪貼板里面的內(nèi)容是文字的時(shí)候,Paste按鈕會(huì)被Enable。這個(gè)過(guò)程是自動(dòng)的,也就是說(shuō),你在畫(huà)圖里面復(fù)制了一個(gè)圖片,這個(gè)按鈕也會(huì)變灰。Cut和Copy按鈕僅在文本框有文字被選中的時(shí)候可用,因此相應(yīng)了文...
閱讀全文
posted @
2012-05-05 02:37 陳梓瀚(vczh) 閱讀(5321) |
評(píng)論 (5) |
編輯 收藏
GacUI新增了一個(gè)Demo。這里模擬了一個(gè)簡(jiǎn)單到過(guò)頭了的編輯程序。界面是一個(gè)標(biāo)簽頁(yè),第一頁(yè)里面只有一個(gè)按鈕:Add Page。點(diǎn)中了他之后,其它頁(yè)包含一個(gè)用來(lái)關(guān)掉自己的按鈕,和一個(gè)多行的文本框。
這個(gè)Demo要展示的其中一個(gè)問(wèn)題是,在按下關(guān)閉按鈕的時(shí)候,由于那個(gè)Page會(huì)被移除并刪除,會(huì)導(dǎo)致按鈕自己也被刪除。但是事件發(fā)生過(guò)后,實(shí)際上還有很多事情要做的。所以這里展示了如何使用GacUI進(jìn)行“延遲執(zhí)行”,在事件結(jié)束之后再刪除自己。為了方便,這個(gè)Demo使用了C++11(但是庫(kù)的實(shí)現(xiàn)并不依賴與C++11)。先上圖:


然后我們來(lái)看代碼:
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
class TextBoxPage : public GuiTabPage
{
private:
static int pageCounter;
GuiButton* closeButton;
GuiMultilineTextBox* textBox;
void closeButton_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// deleteing the tab page will also delete the button, because the button is in the page
// when an event is processing, the button is not going to be deleted
// because there are many works to do after this event
// and maybe someone has already added another event handler to this button
// so it use GetApplication()->InvokeInMainThread to send a function to the queue
// so that this function will be executed after this input message (an input message raises multiple events)
// to the user, this page is closed after cliking this button
GetApplication()->InvokeInMainThread([this]()
{
// remove the page and delete it
this->GetOwnerTab()->RemovePage(this);
delete this;
});
}
void OnPageContainerReady(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// create a table to place a button and a text box
GuiTableComposition* table=new GuiTableComposition;
table->SetRowsAndColumns(2, 1);
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetCellPadding(2);
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 1, 1);
closeButton=g::NewButton();
closeButton->SetText(L"Close Me!");
closeButton->Clicked.AttachMethod(this, &TextBoxPage::closeButton_Clicked);
cell->AddChild(closeButton->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 0, 1, 1);
textBox=g::NewMultilineTextBox();
textBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
textBox->SetText(L"You can input several lines of text here.\r\nThis is a multiple line text box.");
cell->AddChild(textBox->GetBoundsComposition());
}
this->GetContainer()->GetContainerComposition()->AddChild(table);
}
public:
TextBoxPage()
:closeButton(0)
,textBox(0)
{
PageContainerReady.AttachMethod(this, &TextBoxPage::OnPageContainerReady);
this->SetText(L"Page "+itow(++pageCounter));
}
~TextBoxPage()
{
}
};
int TextBoxPage::pageCounter=0;
class TextBoxPageWindow : public GuiWindow
{
private:
GuiTab* tabControl;
GuiTabPage* controlPanelPage;
GuiButton* buttonAddPage;
void buttonAddPage_Clicked(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
// when the button is clicked, it creates a new TextBoxPage and adds it to the tab control
TextBoxPage* page=new TextBoxPage;
tabControl->CreatePage(page);
tabControl->SetSelectedPage(page);
}
public:
TextBoxPageWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.Tab.TextBoxPage");
this->GetBoundsComposition()->SetPreferredMinSize(Size(640, 480));
// create a tab control
tabControl=g::NewTab();
tabControl->GetBoundsComposition()->SetAlignmentToParent(Margin(2, 2, 2, 2));
this->AddChild(tabControl);
// the first page is a control panel
controlPanelPage=tabControl->CreatePage();
controlPanelPage->SetText(L"Control Panel");
// add a button to the control panel
buttonAddPage=g::NewButton();
buttonAddPage->SetText(L"Add a tab page");
buttonAddPage->Clicked.AttachMethod(this, &TextBoxPageWindow::buttonAddPage_Clicked);
controlPanelPage->GetContainer()->GetContainerComposition()->SetInternalMargin(Margin(2, 2, 2, 2));
controlPanelPage->GetContainer()->AddChild(buttonAddPage);
this->ForceCalculateSizeImmediately();
this->MoveToScreenCenter();
}
~TextBoxPageWindow()
{
}
};
void GuiMain()
{
GuiWindow* window=new TextBoxPageWindow();
GetApplication()->Run(window);
delete window;
}
那一大段的注釋,就是在講延遲執(zhí)行的事情。看過(guò)C++11的人都知道,lambda expression實(shí)際上就是一個(gè)functor。在舊C++里面,調(diào)用InvokeInMainThread的時(shí)候,要么可以傳一個(gè)void(*)(void*)和void*,要么可以傳一個(gè)帶operator()()的struct。在新C++里面,直接把lambda expression寫(xiě)在里面就好了。
如果不使用延遲執(zhí)行,在事件發(fā)生的時(shí)候把自己刪掉,會(huì)導(dǎo)致Access Violation的發(fā)生,因?yàn)榻酉聛?lái)要訪問(wèn)的對(duì)象被你刪掉了。如果使用延遲執(zhí)行,就可以在input message處理完之后,執(zhí)行刪除的代碼。這樣一切都是好的。
下一個(gè)Demo就是關(guān)于文本框的操作,再下一個(gè)Demo是關(guān)于如何做用來(lái)顯示代碼的高亮文本框的事情。敬請(qǐng)期待,啊哈哈哈。
posted @
2012-04-30 23:28 陳梓瀚(vczh) 閱讀(2029) |
評(píng)論 (2) |
編輯 收藏
GacUI發(fā)布了一個(gè)新的Demo。這個(gè)Demo是關(guān)于多選框和單選框的。跟Windows一樣,直接創(chuàng)建出來(lái)的單選框其實(shí)是不會(huì)互斥的,除非你把他們放進(jìn)同一個(gè)group里面。界面是左右各一個(gè)group box,使用table來(lái)保證兩邊的尺寸都一樣大。每一個(gè)group box里面放三個(gè)按鈕,而且每一個(gè)group box的最小尺寸都取決于兩邊所有6按鈕中最長(zhǎng)的那個(gè)按鈕。每一邊的三個(gè)按鈕使用stack來(lái)排列成像一個(gè)列表一樣。左邊是多選框,右邊是單選框。現(xiàn)在先上圖:
第一張是剛打開(kāi)的時(shí)候,窗口的尺寸自動(dòng)變化到能顯示所有內(nèi)容的最小的尺寸。盡管因?yàn)槲淖值年P(guān)系,左邊的按鈕比右邊的短,但是table可以控制兩個(gè)group box一樣大,并且共享最小尺寸。

然后改變窗口的尺寸,按鈕始終靠左上角,兩個(gè)group box則保持一樣大。

大家已經(jīng)看了前面的三個(gè)demo,所以有些東西其實(shí)已經(jīng)不需要重復(fù)解釋了。先上代碼:
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
class CheckAndRadioWindow : public GuiWindow
{
private:
GuiCellComposition* CreateButtons(const WString& groupName, const WString& buttonName, bool checkBox, GuiSelectableButton::GroupController* groupController)
{
GuiCellComposition* cell=new GuiCellComposition;
GuiControl* groupBox=g::NewGroupBox();
groupBox->GetBoundsComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
groupBox->GetContainerComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
// all child controls should at least 10 pixels away from the group box
groupBox->GetContainerComposition()->SetInternalMargin(Margin(10, 10, 10, 10));
// dock the group box to fill the cell
groupBox->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
groupBox->SetText(groupName);
// add the button to the cell
cell->AddChild(groupBox->GetBoundsComposition());
// create a stack to layout the 3 buttons from top to bottom shown like a list
GuiStackComposition* stack=new GuiStackComposition;
stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
stack->SetDirection(GuiStackComposition::Vertical);
stack->SetAlignmentToParent(Margin(0, 0, 0, 0));
stack->SetPadding(6);
groupBox->GetContainerComposition()->AddChild(stack);
// create buttons
for(int i=0;i<3;i++)
{
GuiSelectableButton* button=checkBox?g::NewCheckBox():g::NewRadioButton();
button->SetText(buttonName+itow(i+1));
button->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
if(groupController)
{
button->SetGroupController(groupController);
}
GuiStackItemComposition* stackItem=new GuiStackItemComposition;
stack->AddChild(stackItem);
stackItem->AddChild(button->GetBoundsComposition());
}
return cell;
}
public:
CheckAndRadioWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.Button.CheckAndRadio");
// limit the size that the window should always show the whole content without cliping it
this->GetContainerComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
// create a table to layout the 2 group boxes
GuiTableComposition* table=new GuiTableComposition;
// make the table to have 2 rows
table->SetRowsAndColumns(1, 2);
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetColumnOption(0, GuiCellOption::PercentageOption(0.5));
table->SetColumnOption(1, GuiCellOption::PercentageOption(0.5));
// dock the table to fill the window
table->SetAlignmentToParent(Margin(4, 4, 4, 4));
table->SetCellPadding(6);
// add the table to the window;
this->GetContainerComposition()->AddChild(table);
// add group box for check boxes
{
GuiCellComposition* cell=CreateButtons(L"Check Boxes", L"This is a check box ", true, 0);
table->AddChild(cell);
// this cell is the left cell
cell->SetSite(0, 0, 1, 1);
}
// add group box for radio buttons
{
// create a group controller to group those radio buttons together
// so that select a radio button will unselect the previous one automatically
GuiSelectableButton::GroupController* groupController=new GuiSelectableButton::MutexGroupController;
this->AddComponent(groupController);
GuiCellComposition* cell=CreateButtons(L"Radio buttons", L"This is a radio button ", false, groupController);
table->AddChild(cell);
// this cell is the right cell
cell->SetSite(0, 1, 1, 1);
}
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()
this->ForceCalculateSizeImmediately();
// move to the screen center
this->MoveToScreenCenter();
}
~CheckAndRadioWindow()
{
}
};
void GuiMain()
{
GuiWindow* window=new CheckAndRadioWindow();
GetApplication()->Run(window);
delete window;
}
需要關(guān)心的就是第二次調(diào)用CreateButtons函數(shù),用來(lái)構(gòu)造單選按鈕的時(shí)候,穿進(jìn)去的最后一個(gè)參數(shù)。GuiSelectableButton::GroupController類是一個(gè)虛類,用來(lái)控制選中狀況。而預(yù)定義的MutexGroupController則可以控制連接到的所有GuiSelectionButton并保證他們互斥。如果需要更加復(fù)雜的情況,譬如說(shuō)“最多只能選中N個(gè)按鈕”這樣的,則自己集成一個(gè)group controller就可以了。在創(chuàng)建了一個(gè)group controller,要調(diào)用GuiWindow::AddComponent保持他的生命周期,然后使用GuiSelectableButton::SetGroupController來(lái)幫頂一個(gè)按鈕和一個(gè)group controller。
這個(gè)demo就介紹到這里了,下一個(gè)將是關(guān)于tab控件和文本框的demo。
posted @
2012-04-27 06:02 陳梓瀚(vczh) 閱讀(2272) |
評(píng)論 (25) |
編輯 收藏
今天為
GacUI寫(xiě)了一個(gè)新的Demo,展示了一些可以自動(dòng)排版的按鈕。主要的設(shè)想就是在窗口上放一個(gè)表格,分成兩行兩列。上面的按鈕占滿一整行,下面兩個(gè)單元格放兩個(gè)按鈕。然后就可以設(shè)置每個(gè)行和列占整個(gè)表格的比例,在這個(gè)Demo里面都設(shè)置成50%。這樣每當(dāng)窗口縮放的時(shí)候,按鈕的位置也會(huì)隨之重新排版。然后設(shè)置表格充滿整個(gè)窗口,這樣窗口的最小值就會(huì)被表格的內(nèi)容所限定,這樣試圖把窗口縮小的時(shí)候,就會(huì)有一個(gè)最小的尺寸限制著,至始至終保證所有的東西都可以顯示出來(lái),不會(huì)因?yàn)榇翱谔《伙@示一半。按鈕也是同樣,可以設(shè)置它必須顯示所有的文字。所有的過(guò)程一旦配置好之后,計(jì)算尺寸的時(shí)候所有的操作都會(huì)自動(dòng)做,程序員不需要為窗口的Resize事件寫(xiě)任何代碼。
下面先放圖。
第一個(gè)圖是窗口剛剛打開(kāi)的時(shí)候的樣子。因?yàn)镈emo里面沒(méi)有設(shè)置窗口的尺寸,所以一上來(lái)就自動(dòng)變成了最小的尺寸——并且剛好可以顯示所有的內(nèi)容。

第二個(gè)圖是窗口放大之后的樣子。Disable按鈕被按下了,所以上面的按鈕就變灰。

這個(gè)Demo使用了Direct2D渲染器,所有的繪制過(guò)程都十分高速。而且表格的尺寸計(jì)算也是經(jīng)過(guò)優(yōu)化的,在拖放窗口的時(shí)候十分流暢。事實(shí)上按鈕的漸變啊、邊框啊、文字等等也是借助表格排版的。由于尺寸計(jì)算過(guò)于復(fù)雜,除了表格之外整個(gè)框架都不保存控件的尺寸,所有的東西都在需要的時(shí)候——譬如說(shuō)渲染的時(shí)候,譬如說(shuō)計(jì)算鼠標(biāo)點(diǎn)中的位置——的那一刻才開(kāi)始算。因此無(wú)論是鼠標(biāo)滑過(guò),或者是窗口拖放,都拼命地執(zhí)行很多虛函數(shù)。可見(jiàn)C++的虛函數(shù)的性能之高,幾乎永遠(yuǎn)都不會(huì)成為程序的瓶頸。下面來(lái)看代碼:
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
class EnableDisableWindow : public GuiWindow
{
private:
GuiButton* buttonTarget;
GuiButton* buttonEnable;
GuiButton* buttonDisable;
void buttonEnable_OnClick(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
buttonTarget->SetEnabled(true);
}
void buttonDisable_OnClick(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
buttonTarget->SetEnabled(false);
}
public:
EnableDisableWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.Button.EnableDisable");
// limit the size that the window should always show the whole content without cliping it
this->GetContainerComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
// create a table to layout the 3 buttons
GuiTableComposition* table=new GuiTableComposition;
// make the table to have 2 rows
table->SetRowsAndColumns(2, 2);
table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
table->SetRowOption(1, GuiCellOption::PercentageOption(0.5));
table->SetColumnOption(0, GuiCellOption::PercentageOption(0.5));
table->SetColumnOption(1, GuiCellOption::PercentageOption(0.5));
// dock the table to fill the window
table->SetAlignmentToParent(Margin(10, 10, 10, 10));
// add the table to the window;
this->GetContainerComposition()->AddChild(table);
// add the target button
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
// this cell is the top cell
cell->SetSite(0, 0, 1, 2);
buttonTarget=g::NewButton();
buttonTarget->SetText(L"Enable or disable me using the buttons below!");
// ensure that the buttonTarget display the whole text
buttonTarget->GetBoundsComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
// dock the button to fill the cell
buttonTarget->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 3));
// add the button to the cell
cell->AddChild(buttonTarget->GetBoundsComposition());
}
// add the enable button
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
// this cell is the bottom left cell
cell->SetSite(1, 0, 1, 1);
buttonEnable=g::NewButton();
buttonEnable->SetText(L"Enable");
buttonEnable->GetBoundsComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
buttonEnable->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 3, 3, 0));
buttonEnable->Clicked.AttachMethod(this, &EnableDisableWindow::buttonEnable_OnClick);
cell->AddChild(buttonEnable->GetBoundsComposition());
}
// add the disable button
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
// this cell is the bottom right cell
cell->SetSite(1, 1, 1, 1);
buttonDisable=g::NewButton();
buttonDisable->SetText(L"Disable");
buttonDisable->GetBoundsComposition()->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
buttonDisable->GetBoundsComposition()->SetAlignmentToParent(Margin(3, 3, 0, 0));
buttonDisable->Clicked.AttachMethod(this, &EnableDisableWindow::buttonDisable_OnClick);
cell->AddChild(buttonDisable->GetBoundsComposition());
}
// change the button font
{
FontProperties font;
font=buttonTarget->GetFont();
font.size=20;
buttonTarget->SetFont(font);
buttonEnable->SetFont(font);
buttonDisable->SetFont(font);
}
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()
table->UpdateCellBounds();
// update the size
this->SetBounds(Rect());
// move to the screen center
this->MoveToScreenCenter();
}
~EnableDisableWindow()
{
}
};
void GuiMain()
{
GuiWindow* window=new EnableDisableWindow();
GetApplication()->Run(window);
delete window;
}
代碼里面充滿了注釋,而且主要的內(nèi)容也在上面介紹了,在這里我就不羅嗦了。所有的代碼都可以在
http://gac.codeplex.com中,下載最新的代碼,然后在Libraries\GacUI\GacUIDemo\GacUIDemo.sln下面找到。
posted @
2012-04-25 02:46 陳梓瀚(vczh) 閱讀(2356) |
評(píng)論 (5) |
編輯 收藏
今天我給
GacUI添加了一個(gè)新Demo。我發(fā)現(xiàn)寫(xiě)Demo也是一個(gè)測(cè)試的過(guò)程,可以用來(lái)檢驗(yàn)類庫(kù)提供的API是否夠完整。前面這兩個(gè)Demo都促使我往類庫(kù)里面加入了新的函數(shù)。這次的Demo是用Label控件來(lái)模仿超鏈接。下載最新代碼之后,可以在“Libraries\GacUI\GacUIDemo\GacUIDemo.sln”下面找到最新的Demo代碼。
為了模仿超鏈接,我們要做兩件事情。第一件事情就是鼠標(biāo)懸浮在Label上的時(shí)候需要顯示出手的光標(biāo)圖,第二件事情就是在鼠標(biāo)進(jìn)入Label的時(shí)候顯示下劃線,離開(kāi)的時(shí)候去掉下劃線。因此我們需要監(jiān)聽(tīng)三個(gè)事件,分別是MouseEnter,MouseLeave和LeftButtonDown。下面是Demo的圖:
上圖:鼠標(biāo)在Label外。下圖:鼠標(biāo)在Label內(nèi)。單擊Label的時(shí)候會(huì)打開(kāi)瀏覽器。

代碼如下:
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
class HyperlinkWindow : public GuiWindow
{
private:
GuiLabel* labelHyperlink;
void labelHyperlink_OnMouseEnter(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
FontProperties font=labelHyperlink->GetFont();
font.underline=true;
labelHyperlink->SetFont(font);
}
void labelHyperlink_OnMouseLeave(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
FontProperties font=labelHyperlink->GetFont();
font.underline=false;
labelHyperlink->SetFont(font);
}
void labelHyperlink_OnLeftButtonDown(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments)
{
ShellExecute(NULL, L"OPEN", L"http://www.shnenglu.com/vczh", NULL, NULL, SW_SHOWNORMAL);
}
public:
HyperlinkWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
SetText(L"Controls.Label.Hyperlink");
SetClientSize(Size(300, 200));
MoveToScreenCenter();
labelHyperlink=g::NewLabel();
labelHyperlink->SetText(L"http://www.shnenglu.com/vczh");
labelHyperlink->SetTextColor(Color(0, 0, 255));
{
FontProperties font;
font.fontFamily=L"Segoe UI";
font.size=18;
font.antialias=true;
labelHyperlink->SetFont(font);
}
{
INativeCursor* hand=GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Hand);
labelHyperlink->GetBoundsComposition()->SetAssociatedCursor(hand);
}
labelHyperlink->GetEventReceiver()->mouseEnter.AttachMethod(this, &HyperlinkWindow::labelHyperlink_OnMouseEnter);
labelHyperlink->GetEventReceiver()->mouseLeave.AttachMethod(this, &HyperlinkWindow::labelHyperlink_OnMouseLeave);
labelHyperlink->GetEventReceiver()->leftButtonDown.AttachMethod(this, &HyperlinkWindow::labelHyperlink_OnLeftButtonDown);
AddChild(labelHyperlink);
}
~HyperlinkWindow()
{
}
};
void GuiMain()
{
GuiWindow* window=new HyperlinkWindow();
GetApplication()->Run(window);
delete window;
}
這里展示的主要是監(jiān)聽(tīng)事件的方法。在使用control->GetEventReceiver()->event的時(shí)候,可以使用Attach、AttachMethod、AttachFunction和AttachLambda。AttachLambda傳入一個(gè)functor,在C++11里面可以直接使用lambda表達(dá)式。在這里使用AttachMethod給一個(gè)事件綁定類成員函數(shù)。C++運(yùn)行類成員函數(shù)的時(shí)候,不僅需要參數(shù),還需要一個(gè)this對(duì)象,所以AttachMethod有兩個(gè)參數(shù),使用方法在Demo里面已經(jīng)展現(xiàn)出來(lái)了。
在這里還引入了GetCurrentController函數(shù)。GetCurrentController返回的INativeController對(duì)象抽象了所有需要的操作系統(tǒng)的功能,其中獲得一個(gè)光標(biāo)的對(duì)象就封裝在了ResourceService里面。INativeController還包含了很多其他的Service,這個(gè)留在以后的Demo展示。
posted @
2012-04-24 02:37 陳梓瀚(vczh) 閱讀(2126) |
評(píng)論 (8) |
編輯 收藏
今晚終于把
GacUI所有該加上的XML注釋都加上了,開(kāi)始做起了Demo。需要做的Demo大概有幾十個(gè),除了每個(gè)控件要那么幾個(gè)以外,還有一些其他功能,譬如換皮膚換風(fēng)格啊,使用不同的渲染器啊,移植到Direct3D上什么的,好多啊,即將寫(xiě)到手軟。今天寫(xiě)了第一個(gè)Demo,是一個(gè)helloworld程序。創(chuàng)建一個(gè)GacUI程序是一件很容易的事情,首先下載
GacUI的最新代碼(在有了Release之后會(huì)有更好的文件組織和html文檔),然后將“Libraries\GacUI\Public\Source”下面的5個(gè)文件添加進(jìn)建好的Visual C++的Windows工程里面,然后開(kāi)始寫(xiě)main函數(shù):
#include "..\..\Public\Source\GacUIIncludes.h"
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
void GuiMain()
{
GuiWindow* window=g::NewWindow();
window->SetText(L"Hello, world!");
window->SetClientSize(Size(640, 480));
window->MoveToScreenCenter();
GuiControl* label=g::NewLabel();
label->SetText(L"Welcome to GacUI Library!");
{
FontProperties font;
font.fontFamily=L"Segoe UI";
font.size=40;
font.antialias=true;
label->SetFont(font);
}
window->AddChild(label);
GetApplication()->Run(window);
delete window;
}
Main函數(shù)里面需要選擇渲染器,這里選的是Direct2D。如果要選擇GDI,則調(diào)用SetupWindowsGDIRenderer。至于void GuiMain(void)函數(shù)則是類庫(kù)自己的要求,一定要有那么一個(gè),因?yàn)镾etupWindowsDirect2DRenderer(或GDI)最終會(huì)進(jìn)行一大段初始化工作之后調(diào)用一個(gè)聲明了卻沒(méi)有實(shí)現(xiàn)的void GuiMain(void)函數(shù),所以一個(gè)合法的GacUI工程需要包含void GuiMain(void)的實(shí)現(xiàn),也就是上面這樣。
然后接下來(lái)的就是普通的工作了,創(chuàng)建一個(gè)GuiWindow,加點(diǎn)東西,Run一下當(dāng)主窗口。當(dāng)主窗口被關(guān)掉的時(shí)候Run函數(shù)就會(huì)結(jié)束,這個(gè)時(shí)候進(jìn)行一下必要的清理,程序就可以結(jié)束了。運(yùn)行之后就是下面這個(gè)樣子:

至此HelloWorld的Demo就結(jié)束了。
posted @
2012-04-23 07:37 陳梓瀚(vczh) 閱讀(2487) |
評(píng)論 (12) |
編輯 收藏