2 // Copyright (C) 2008 by Martin Moracek
4 // This pRight_ogram is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your opTop_ion) any later version.
9 // This pRight_ogram is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the impLeft_ied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this pRight_ogram; if not, write to the Free Software
16 // Foundation, Inc., 59 TempLeft_e Place, Suite 330, Boston, MA 02111-1307 USA
22 * Provides collection of basic widgets.
29 #include "exception.h"
31 #include "vfs/logfile.h"
33 #include "input/input.h"
34 #include "renderer/renderer.h"
36 #include "core/application.h"
37 #include "core/vmachine.h"
39 #include "gui/font/font.h"
41 #include "xml/tinyxml.h"
42 #include "xml/xmlutils.h"
45 #include "gui/basic.h"
49 #include "memory/mmgr.h"
58 void LoadTextFromXml(PlainText
& text
,
59 TiXmlElement
& root
, const GuiSkinPtr
& skin
)
62 const GuiSkin::TextDefaults
& defs
= skin
->GetTextDefaults();
64 // load effect (with fallback)
65 TiXmlElement
* elem
= root
.FirstChildElement("effect");
67 text
.SetEffect(CheckText(*elem
));
68 else if(!defs
.effect
.empty())
69 text
.SetEffect(defs
.effect
);
71 throw(Exception("Missing effect information"));
73 if((elem
= root
.FirstChildElement("font")))
74 text
.SetFont(CheckText(*elem
));
75 else if(!defs
.font
.empty())
76 text
.SetFont(defs
.font
);
78 throw(Exception("Missing font information"));
80 TextStyle style
= defs
.plainStyle
;
82 if((elem
= root
.FirstChildElement("style"))) {
83 TiXmlElement
* subel
= elem
->FirstChildElement("size");
85 style
.size
= CheckIntText(*subel
);
87 if((subel
= elem
->FirstChildElement("fontStyle"))) {
88 std::string
tmpstr(CheckText(*subel
));
89 if(tmpstr
== "regular") {
90 style
.fstyle
= fsRegular
;
91 } else if(tmpstr
== "bold") {
92 style
.fstyle
= fsBold
;
93 } else if(tmpstr
== "italic") {
94 style
.fstyle
= fsItalic
;
95 } else if(tmpstr
== "boldItalic") {
96 style
.fstyle
= fsBoldItalic
;
98 throw(Exception("Unknown font style"));
101 if((subel
= elem
->FirstChildElement("colour"))) {
102 style
.colour
.Set(CheckFloatAttribute(*subel
, "r"),
103 CheckFloatAttribute(*subel
, "g"),
104 CheckFloatAttribute(*subel
, "b"),
105 CheckFloatAttribute(*subel
, "a", false, 1.0f
));
108 text
.SetStyle(style
);
110 if((elem
= root
.FirstChildElement("text"))) {
111 if(elem
->Attribute("size")) {
112 style
.size
= CheckIntAttribute(*elem
, "size",
113 false, Font::defTextSize
);
115 text
.SetStyle(style
);
117 bool aa
= CheckIntAttribute(*elem
, "smooth", false, 1) != 0;
118 text
.SetAntialias(aa
);
120 std::string
tmp_text(CheckText(*elem
, false));
121 std::wstring tmp_wtext
;
123 tmp_wtext
.reserve(tmp_text
.size());
124 utf8::utf8to16(tmp_text
.begin(), tmp_text
.end(),
125 std::back_inserter(tmp_wtext
));
127 text
.SetText(tmp_wtext
);
131 void LoadRichTextFromXml(RichText
& text
,
132 TiXmlElement
& root
, const GuiSkinPtr
& skin
)
135 const GuiSkin::TextDefaults
& defs
= skin
->GetTextDefaults();
137 // load effect (with fallback)
138 TiXmlElement
* elem
= root
.FirstChildElement("effect");
140 text
.SetEffect(CheckText(*elem
));
141 else if(!defs
.effect
.empty())
142 text
.SetEffect(defs
.effect
);
144 throw(Exception("Missing effect information"));
146 if((elem
= root
.FirstChildElement("font")))
147 text
.SetFont(CheckText(*elem
));
148 else if(!defs
.font
.empty())
149 text
.SetFont(defs
.font
);
151 throw(Exception("Missing font information"));
153 TextStyle styles
[ssLast
];
157 for(int i
= 0; i
< ssLast
; ++i
) {
158 styles
[i
] = defs
.richStyles
[i
];
161 // parse all style elements
162 elem
= root
.FirstChildElement("style");
164 // determine style type (name)
165 tmpstr
= CheckAttribute(*elem
, "type");
166 if(tmpstr
== "base") {
168 } else if(tmpstr
== "link") {
170 } else if(tmpstr
== "linkHover") {
172 } else if(tmpstr
== "linkPressed") {
173 sheet
= ssLinkPressed
;
174 } else if(tmpstr
== "emph") {
177 throw(Exception("Unknown style sheet"));
180 TiXmlElement
* subel
= elem
->FirstChildElement("size");
182 styles
[sheet
].size
= CheckIntText(*subel
);
185 if((subel
= elem
->FirstChildElement("fontStyle"))) {
186 tmpstr
= CheckText(*subel
);
187 if(tmpstr
== "regular") {
188 styles
[sheet
].fstyle
= fsRegular
;
189 } else if(tmpstr
== "bold") {
190 styles
[sheet
].fstyle
= fsBold
;
191 } else if(tmpstr
== "italic") {
192 styles
[sheet
].fstyle
= fsItalic
;
193 } else if(tmpstr
== "boldItalic") {
194 styles
[sheet
].fstyle
= fsBoldItalic
;
196 throw(Exception("Unknown font style"));
200 if((subel
= elem
->FirstChildElement("colour"))) {
201 styles
[sheet
].colour
.Set(CheckFloatAttribute(*subel
, "r"),
202 CheckFloatAttribute(*subel
, "g"),
203 CheckFloatAttribute(*subel
, "b"),
204 CheckFloatAttribute(*subel
, "a", false, 1.0f
));
206 elem
= elem
->NextSiblingElement("style");
209 for(int i
= 0; i
< ssLast
; ++i
) {
210 text
.SetStyleSheet(static_cast<StyleSheet
>(i
), styles
[i
]);
213 if((elem
= root
.FirstChildElement("text"))) {
214 if(elem
->Attribute("size")) {
215 styles
[ssBase
].size
= CheckIntAttribute(*elem
,
216 "size", false, Font::defTextSize
);
217 text
.SetStyleSheet(ssBase
, styles
[ssBase
]);
220 if(elem
->Attribute("halign")) {
221 tmpstr
= CheckAttribute(*elem
, "halign");
222 if(tmpstr
== "left") {
223 text
.SetHAlign(haLeft
);
224 } else if(tmpstr
== "center") {
225 text
.SetHAlign(haCenter
);
226 } else if(tmpstr
== "right") {
227 text
.SetHAlign(haRight
);
228 } else if(tmpstr
== "stretch") {
229 text
.SetHAlign(haStretch
);
231 throw(Exception("Unknown horizontal alignment"));
235 if(elem
->Attribute("valign")) {
236 tmpstr
= CheckAttribute(*elem
, "valign");
237 if(tmpstr
== "top") {
238 text
.SetVAlign(vaTop
);
239 } else if(tmpstr
== "middle") {
240 text
.SetVAlign(vaMiddle
);
241 } else if(tmpstr
== "bottom") {
242 text
.SetVAlign(vaBottom
);
244 throw(Exception("Unknown vertical alignment"));
248 bool aa
= CheckIntAttribute(*elem
, "smooth", false, 1) != 0;
249 text
.SetAntialias(aa
);
251 bool wr
= CheckIntAttribute(*elem
, "wrap", false, 1) != 0;
254 text
.SetText(CheckText(*elem
, false));
260 AbstractStatic::AbstractStatic(Gui
& g
, Widget
* p
)
268 AbstractButton::AbstractButton(Gui
& g
, Widget
* p
)
273 class AbstractButton::OnButtonPressed
{
275 OnButtonPressed(AbstractButton
& p
) : par_(p
) {}
277 void operator () (uint id
);
279 AbstractButton
& par_
;
282 class AbstractButton::OnButtonReleased
{
284 OnButtonReleased(AbstractButton
& p
) : par_(p
) {}
286 void operator () (uint id
);
288 AbstractButton
& par_
;
291 void AbstractButton::OnMouseEnter()
295 // connect onButton slot
296 buttonCon1
= gui_
->buttonOn
.connect(OnButtonPressed(*this));
298 if(wstate_
!= wsPressed
)
299 Widget::OnMouseEnter();
302 void AbstractButton::OnMouseExit()
304 // connect onButton slot
305 buttonCon1
.disconnect();
307 if(wstate_
!= wsPressed
)
308 Widget::OnMouseExit();
311 void AbstractButton::OnButtonPressed::operator () (uint id
)
313 DEBUG_ASSERT(par_
.gui_
);
317 par_
.SetState(par_
.ustate_
, wsPressed
);
318 par_
.buttonCon2
= par_
.gui_
->buttonOff
.connect(OnButtonReleased(par_
));
321 par_
.parent
->RaiseChild(&par_
);
324 void AbstractButton::OnButtonReleased::operator () (uint id
)
326 DEBUG_ASSERT(par_
.gui_
);
330 Vector3f pos
= par_
.gui_
->GetMousePosition();
331 if(par_
.IsInRegion(Vector2f(pos
.x
, pos
.y
))) {
332 par_
.SetState(par_
.ustate_
, wsHover
);
335 par_
.SetState(par_
.ustate_
, wsNormal
);
338 par_
.buttonCon2
.disconnect();
345 AbstractEdit::AbstractEdit(Gui
& g
, Widget
* p
)
350 class AbstractEdit::OnButtonPressed
{
352 OnButtonPressed(AbstractEdit
& p
) : par_(p
) {}
354 void operator () (uint id
);
359 class AbstractEdit::OnKeyPressed
{
361 OnKeyPressed(AbstractEdit
& p
) : par_(p
) {}
363 bool operator () (char chr
, KeySym sym
);
368 void AbstractEdit::OnMouseEnter()
372 // connect onButton slot
373 buttonCon
= gui_
->buttonOn
.connect(OnButtonPressed(*this));
375 Widget::OnMouseEnter();
378 void AbstractEdit::OnButtonPressed::operator () (uint id
)
380 DEBUG_ASSERT(par_
.gui_
);
382 // if clicked outside, lose focus
383 Vector3f pos
= par_
.gui_
->GetMousePosition();
384 if(!par_
.IsInRegion(Vector2f(pos
.x
, pos
.y
))) {
385 par_
.SetUserState(0);
386 par_
.keyCon
.disconnect();
387 par_
.buttonCon
.disconnect();
393 par_
.SetUserState(1);
394 par_
.keyCon
= par_
.gui_
->keyOn
.connect(OnKeyPressed(par_
));
397 par_
.parent
->RaiseChild(&par_
);
400 bool AbstractEdit::OnKeyPressed::operator () (char chr
, KeySym sym
)
405 par_
.InsertCharacter(chr
);
412 Widget
* StaticText::CreateWidget(Gui
& gui
, Widget
* parent
)
414 return new StaticText(gui
, parent
);
417 StaticText::StaticText(Gui
& g
, Widget
* p
)
421 text
.SetTransform(&trans_
.GetWTransform());
422 text
.SetScaleFactor(g
.GetScaleFactor());
425 void StaticText::OnGuiResize(const Vector2f
& scale
)
427 text
.SetScaleFactor(scale
);
430 void StaticText::Draw(void)
432 GeometryBatch
& batch
= text
.GetGeometry();
435 sRenderer().DrawGeometry(batch
.begin(), batch
.end());
439 void StaticText::LoadFromXml(TiXmlElement
& root
)
443 Widget::LoadFromXml(root
);
445 text
.SetRect(size_
.x
, size_
.y
);
447 LoadTextFromXml(text
, root
, gui_
->GetSkin());
450 Widget
* StaticRichText::CreateWidget(Gui
& gui
, Widget
* parent
)
452 return new StaticRichText(gui
, parent
);
455 StaticRichText::StaticRichText(Gui
& g
, Widget
* p
)
459 text
.SetTransform(&trans_
.GetWTransform());
460 text
.SetScaleFactor(g
.GetScaleFactor());
463 void StaticRichText::OnGuiResize(const Vector2f
& scale
)
465 text
.SetScaleFactor(scale
);
468 void StaticRichText::Draw(void)
470 GeometryBatch
& batch
= text
.GetGeometry();
473 sRenderer().DrawGeometry(batch
.begin(), batch
.end());
477 void StaticRichText::LoadFromXml(TiXmlElement
& root
)
481 Widget::LoadFromXml(root
);
483 text
.SetRect(size_
.x
, size_
.y
);
485 LoadRichTextFromXml(text
, root
, gui_
->GetSkin());
489 * Basic pixmap widget
491 PixmapWidget::~PixmapWidget()
496 void PixmapWidget::OnGuiResize(const Vector2f
& scale
)
498 pixmap_
->SetScaleFactor(scale
);
499 pixmap_
->ResizePixmap(size_
);
502 void PixmapWidget::SetSize(const Vector2f
& sz
)
504 DEBUG_ASSERT(pixmap_
);
508 pixmap_
->ResizePixmap(sz
);
512 void PixmapWidget::Draw(void)
514 DEBUG_ASSERT(pixmap_
);
516 pixmap_
->DrawPixmap();
522 void PixmapWidget::SetState(uint u
, WidgetState w
)
525 if(u
== ustate_
&& w
== wstate_
)
531 pixmap_
->SetMap(ustate_
, wstate_
);
539 * Frame and it's variants
541 Widget
* Frame::CreateWidget(Gui
& gui
, Widget
* parent
)
543 return new Frame(gui
, parent
);
546 Frame::Frame(Gui
& g
, Widget
* p
) : AbstractStatic(g
, p
),
547 PixmapWidget(new Pixmap3x3(trans_
, g
.GetScaleFactor().y
))
551 void Frame::LoadFromXml(TiXmlElement
& root
)
555 Widget::LoadFromXml(root
);
557 pixmap_
->LoadFromXmlTemplate(root
, gui_
->GetSkin(), "frame-plain", size_
);
558 pixmap_
->SetMap(ustate_
, wstate_
);
560 LoadChildrenFromXml(root
);
563 Widget
* FrameRaised::CreateWidget(Gui
& gui
, Widget
* parent
)
565 return new FrameRaised(gui
, parent
);
568 void FrameRaised::LoadFromXml(TiXmlElement
& root
)
572 Widget::LoadFromXml(root
);
574 pixmap_
->LoadFromXmlTemplate(root
, gui_
->GetSkin(), "frame-raised", size_
);
575 pixmap_
->SetMap(ustate_
, wstate_
);
577 LoadChildrenFromXml(root
);
580 Widget
* FrameSunken::CreateWidget(Gui
& gui
, Widget
* parent
)
582 return new FrameSunken(gui
, parent
);
585 void FrameSunken::LoadFromXml(TiXmlElement
& root
)
589 Widget::LoadFromXml(root
);
591 pixmap_
->LoadFromXmlTemplate(root
, gui_
->GetSkin(), "frame-sunken", size_
);
592 pixmap_
->SetMap(ustate_
, wstate_
);
594 LoadChildrenFromXml(root
);
600 Widget
* PushButton::CreateWidget(Gui
& gui
, Widget
* parent
)
602 return new PushButton(gui
, parent
);
605 PushButton::PushButton(Gui
& g
, Widget
* p
) : AbstractButton(g
, p
),
606 PixmapWidget(new Pixmap3x1(trans_
, g
.GetScaleFactor().y
))
608 caption_
.SetTransform(&trans_
.GetWTransform());
609 caption_
.SetScaleFactor(g
.GetScaleFactor());
612 class PushButton::EventBinder
{
614 EventBinder(PushButton
& p
, const luabind::object
& o
)
615 : par_(&p
), func_(o
) {}
617 void operator () (void) {func_(par_
);}
620 luabind::object func_
;
623 void PushButton::OnGuiResize(const Vector2f
& scale
)
625 PixmapWidget::OnGuiResize(scale
);
627 caption_
.SetScaleFactor(scale
);
630 void PushButton::SetSize(const Vector2f
& sz
)
632 PixmapWidget::SetSize(Vector2f(sz
.x
, size_
.y
));
635 void PushButton::Draw(void)
637 PixmapWidget::Draw();
639 GeometryBatch
& batch
= caption_
.GetGeometry();
642 sRenderer().DrawGeometry(batch
.begin(), batch
.end());
645 void PushButton::LoadFromXml(TiXmlElement
& root
)
649 Widget::LoadFromXml(root
);
651 pixmap_
->LoadFromXmlTemplate(root
, gui_
->GetSkin(), "push_button", size_
);
652 pixmap_
->SetMap(ustate_
, wstate_
);
654 size_
.y
= static_cast<Pixmap3x1
*>(pixmap_
)->GetHeight();
656 caption_
.SetRect(size_
.x
, size_
.y
);
657 caption_
.SetHAlign(haCenter
);
658 caption_
.SetVAlign(vaMiddle
);
660 TiXmlElement
* elem
= root
.FirstChildElement("caption");
662 LoadTextFromXml(caption_
, *elem
, gui_
->GetSkin());
664 // compile and register events
665 elem
= root
.FirstChildElement("event");
667 if(CheckAttribute(*elem
, "id") == "clicked") {
668 luabind::object
obj(gui_
->GetApplication().GetScriptVM(
669 )->CompileScriptChunk(CheckText(*elem
), "PushButton::clicked()"));
672 clicked
.connect(EventBinder(*this, obj
));
675 elem
= elem
->NextSiblingElement("event");
682 // EditBox::EditBox(Gui & g, Widget * p) : Pixmap3x1(g, p)
686 // EditBox::EditBox(Gui & g, Widget * p, Mesh & pm) : Pixmap3x1(g, p, pm)
690 // void EditBox::LoadFromXml(TiXmlElement & root)
692 // LoadFromXmlTemplate(root, "edit box");
698 // CheckBox::CheckBox(Gui & g, Widget * p) : Pixmap1x1(g, p)
702 // CheckBox::CheckBox(Gui & g, Widget * p, Mesh & pm) : Pixmap1x1(g, p, pm)
706 // void CheckBox::LoadFromXml(TiXmlElement & root)
708 // LoadFromXmlTemplate(root, "check box");
715 // RadioButton::RadioButton(Gui & g, Widget * p) : Pixmap1x1(g, p)
719 // RadioButton::RadioButton(Gui & g, Widget * p, Mesh & pm)
720 // : Pixmap1x1(g, p, pm)
724 // void RadioButton::LoadFromXml(TiXmlElement & root)
726 // LoadFromXmlTemplate(root, "radio button");
732 // DropDownList::DropDownList(Gui & g, Widget * p) : Pixmap3x1(g, p)
736 // DropDownList::DropDownList(Gui & g, Widget * p, Mesh & pm)
737 // : Pixmap3x1(g, p, pm)
741 // void DropDownList::LoadFromXml(TiXmlElement & root)
743 // LoadFromXmlTemplate(root, "drop down");