Initial commit, includes Lua with broken Luabind as a backup for branching purposes
[terrastrategy.git] / src / gui / basic.cpp
blob6655b98719c043f346a0cd654518b352c82c43fa
1 //
2 // Copyright (C) 2008 by Martin Moracek
3 //
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.
8 //
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
19 /**
20 * @file basic.cpp
22 * Provides collection of basic widgets.
25 #include <algorithm>
26 #include <functional>
28 #include "utf8.h"
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"
44 #include "gui/gui.h"
45 #include "gui/basic.h"
47 #include "skin.h"
49 #include "memory/mmgr.h"
51 namespace tre {
54 * Utility functions
56 namespace {
58 void LoadTextFromXml(PlainText & text,
59 TiXmlElement & root, const GuiSkinPtr & skin)
61 DEBUG_ASSERT(skin);
62 const GuiSkin::TextDefaults & defs = skin->GetTextDefaults();
64 // load effect (with fallback)
65 TiXmlElement * elem = root.FirstChildElement("effect");
66 if(elem)
67 text.SetEffect(CheckText(*elem));
68 else if(!defs.effect.empty())
69 text.SetEffect(defs.effect);
70 else
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);
77 else
78 throw(Exception("Missing font information"));
80 TextStyle style = defs.plainStyle;
82 if((elem = root.FirstChildElement("style"))) {
83 TiXmlElement * subel = elem->FirstChildElement("size");
84 if(subel)
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;
97 } else {
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)
134 DEBUG_ASSERT(skin);
135 const GuiSkin::TextDefaults & defs = skin->GetTextDefaults();
137 // load effect (with fallback)
138 TiXmlElement * elem = root.FirstChildElement("effect");
139 if(elem)
140 text.SetEffect(CheckText(*elem));
141 else if(!defs.effect.empty())
142 text.SetEffect(defs.effect);
143 else
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);
150 else
151 throw(Exception("Missing font information"));
153 TextStyle styles[ssLast];
154 StyleSheet sheet;
155 std::string tmpstr;
157 for(int i = 0; i < ssLast; ++i) {
158 styles[i] = defs.richStyles[i];
161 // parse all style elements
162 elem = root.FirstChildElement("style");
163 while(elem) {
164 // determine style type (name)
165 tmpstr = CheckAttribute(*elem, "type");
166 if(tmpstr == "base") {
167 sheet = ssBase;
168 } else if(tmpstr == "link") {
169 sheet = ssLink;
170 } else if(tmpstr == "linkHover") {
171 sheet = ssLinkHover;
172 } else if(tmpstr == "linkPressed") {
173 sheet = ssLinkPressed;
174 } else if(tmpstr == "emph") {
175 sheet = ssEmph;
176 } else {
177 throw(Exception("Unknown style sheet"));
180 TiXmlElement * subel = elem->FirstChildElement("size");
181 if(subel) {
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;
195 } else {
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);
230 } else {
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);
243 } else {
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;
252 text.SetWrap(wr);
254 text.SetText(CheckText(*elem, false));
260 AbstractStatic::AbstractStatic(Gui & g, Widget * p)
262 Init(g, p);
266 * Abstract button
268 AbstractButton::AbstractButton(Gui & g, Widget * p)
270 Init(g, p);
273 class AbstractButton::OnButtonPressed {
274 public:
275 OnButtonPressed(AbstractButton & p) : par_(p) {}
277 void operator () (uint id);
278 private:
279 AbstractButton & par_;
282 class AbstractButton::OnButtonReleased {
283 public:
284 OnButtonReleased(AbstractButton & p) : par_(p) {}
286 void operator () (uint id);
287 private:
288 AbstractButton & par_;
291 void AbstractButton::OnMouseEnter()
293 DEBUG_ASSERT(gui_);
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_);
315 // left mouse button
316 if(id == 0) {
317 par_.SetState(par_.ustate_, wsPressed);
318 par_.buttonCon2 = par_.gui_->buttonOff.connect(OnButtonReleased(par_));
320 if(par_.parent)
321 par_.parent->RaiseChild(&par_);
324 void AbstractButton::OnButtonReleased::operator () (uint id)
326 DEBUG_ASSERT(par_.gui_);
328 // left mouse button
329 if(id == 0) {
330 Vector3f pos = par_.gui_->GetMousePosition();
331 if(par_.IsInRegion(Vector2f(pos.x, pos.y))) {
332 par_.SetState(par_.ustate_, wsHover);
333 par_.clicked();
334 } else {
335 par_.SetState(par_.ustate_, wsNormal);
338 par_.buttonCon2.disconnect();
343 * Abstract edit box
345 AbstractEdit::AbstractEdit(Gui & g, Widget * p)
347 Init(g, p);
350 class AbstractEdit::OnButtonPressed {
351 public:
352 OnButtonPressed(AbstractEdit & p) : par_(p) {}
354 void operator () (uint id);
355 private:
356 AbstractEdit & par_;
359 class AbstractEdit::OnKeyPressed {
360 public:
361 OnKeyPressed(AbstractEdit & p) : par_(p) {}
363 bool operator () (char chr, KeySym sym);
364 private:
365 AbstractEdit & par_;
368 void AbstractEdit::OnMouseEnter()
370 DEBUG_ASSERT(gui_);
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();
388 return;
390 // left mouse button
391 if(id == 0) {
392 // set focus
393 par_.SetUserState(1);
394 par_.keyCon = par_.gui_->keyOn.connect(OnKeyPressed(par_));
396 if(par_.parent)
397 par_.parent->RaiseChild(&par_);
400 bool AbstractEdit::OnKeyPressed::operator () (char chr, KeySym sym)
402 if(chr == 0)
403 return false;
405 par_.InsertCharacter(chr);
406 return true;
410 * Static text widget
412 Widget * StaticText::CreateWidget(Gui & gui, Widget * parent)
414 return new StaticText(gui, parent);
417 StaticText::StaticText(Gui & g, Widget * p)
419 Init(g, 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();
434 if(!batch.empty()) {
435 sRenderer().DrawGeometry(batch.begin(), batch.end());
439 void StaticText::LoadFromXml(TiXmlElement & root)
441 DEBUG_ASSERT(gui_);
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)
457 Init(g, 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();
472 if(!batch.empty()) {
473 sRenderer().DrawGeometry(batch.begin(), batch.end());
477 void StaticRichText::LoadFromXml(TiXmlElement & root)
479 DEBUG_ASSERT(gui_);
481 Widget::LoadFromXml(root);
483 text.SetRect(size_.x, size_.y);
485 LoadRichTextFromXml(text, root, gui_->GetSkin());
489 * Basic pixmap widget
491 PixmapWidget::~PixmapWidget()
493 delete pixmap_;
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_);
506 if(sz != size_) {
507 Widget::SetSize(sz);
508 pixmap_->ResizePixmap(sz);
512 void PixmapWidget::Draw(void)
514 DEBUG_ASSERT(pixmap_);
516 pixmap_->DrawPixmap();
518 // draw children
519 DrawChildren();
522 void PixmapWidget::SetState(uint u, WidgetState w)
524 // no change
525 if(u == ustate_ && w == wstate_)
526 return;
528 ustate_ = u;
529 wstate_ = w;
531 pixmap_->SetMap(ustate_, wstate_);
535 * Basic widgets
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)
553 DEBUG_ASSERT(gui_);
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)
570 DEBUG_ASSERT(gui_);
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)
587 DEBUG_ASSERT(gui_);
589 Widget::LoadFromXml(root);
591 pixmap_->LoadFromXmlTemplate(root, gui_->GetSkin(), "frame-sunken", size_);
592 pixmap_->SetMap(ustate_, wstate_);
594 LoadChildrenFromXml(root);
598 * Push button
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 {
613 public:
614 EventBinder(PushButton & p, const luabind::object & o)
615 : par_(&p), func_(o) {}
617 void operator () (void) {func_(par_);}
618 private:
619 PushButton * 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();
641 if(!batch.empty())
642 sRenderer().DrawGeometry(batch.begin(), batch.end());
645 void PushButton::LoadFromXml(TiXmlElement & root)
647 DEBUG_ASSERT(gui_);
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");
661 if(elem)
662 LoadTextFromXml(caption_, *elem, gui_->GetSkin());
664 // compile and register events
665 elem = root.FirstChildElement("event");
666 while(elem) {
667 if(CheckAttribute(*elem, "id") == "clicked") {
668 luabind::object obj(gui_->GetApplication().GetScriptVM(
669 )->CompileScriptChunk(CheckText(*elem), "PushButton::clicked()"));
671 if(obj.is_valid()) {
672 clicked.connect(EventBinder(*this, obj));
675 elem = elem->NextSiblingElement("event");
680 * Edit box
682 // EditBox::EditBox(Gui & g, Widget * p) : Pixmap3x1(g, p)
683 // {
684 // }
686 // EditBox::EditBox(Gui & g, Widget * p, Mesh & pm) : Pixmap3x1(g, p, pm)
687 // {
688 // }
690 // void EditBox::LoadFromXml(TiXmlElement & root)
691 // {
692 // LoadFromXmlTemplate(root, "edit box");
693 // }
696 * Check box
698 // CheckBox::CheckBox(Gui & g, Widget * p) : Pixmap1x1(g, p)
699 // {
700 // }
702 // CheckBox::CheckBox(Gui & g, Widget * p, Mesh & pm) : Pixmap1x1(g, p, pm)
703 // {
704 // }
706 // void CheckBox::LoadFromXml(TiXmlElement & root)
707 // {
708 // LoadFromXmlTemplate(root, "check box");
709 // }
712 * Radio button
715 // RadioButton::RadioButton(Gui & g, Widget * p) : Pixmap1x1(g, p)
716 // {
717 // }
719 // RadioButton::RadioButton(Gui & g, Widget * p, Mesh & pm)
720 // : Pixmap1x1(g, p, pm)
721 // {
722 // }
724 // void RadioButton::LoadFromXml(TiXmlElement & root)
725 // {
726 // LoadFromXmlTemplate(root, "radio button");
727 // }
730 * Drop-down list
732 // DropDownList::DropDownList(Gui & g, Widget * p) : Pixmap3x1(g, p)
733 // {
734 // }
736 // DropDownList::DropDownList(Gui & g, Widget * p, Mesh & pm)
737 // : Pixmap3x1(g, p, pm)
738 // {
739 // }
741 // void DropDownList::LoadFromXml(TiXmlElement & root)
742 // {
743 // LoadFromXmlTemplate(root, "drop down");
744 // }