1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "GuiContainer.h"
5 
6 #include "Gui.h"
7 #include "graphics/Renderer.h"
8 
9 #include <SDL_stdinc.h>
10 
11 namespace Gui {
12 
Container()13 	Container::Container()
14 	{
15 		m_transparent = true;
16 		SetBgColor(Theme::Colors::bg);
17 		onMouseLeave.connect(sigc::mem_fun(this, &Container::_OnMouseLeave));
18 		onSetSize.connect(sigc::mem_fun(this, &Container::_OnSetSize));
19 	}
20 
~Container()21 	Container::~Container()
22 	{
23 		DeleteAllChildren();
24 	}
25 
_OnSetSize()26 	void Container::_OnSetSize()
27 	{
28 		if (IsVisible()) UpdateAllChildSizes();
29 	}
30 
_OnMouseLeave()31 	void Container::_OnMouseLeave()
32 	{
33 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
34 			if ((*i).w->IsMouseOver() == true)
35 				(*i).w->OnMouseLeave();
36 		}
37 	}
38 
OnMouseMotion(MouseMotionEvent * e)39 	bool Container::OnMouseMotion(MouseMotionEvent *e)
40 	{
41 		float x = e->x;
42 		float y = e->y;
43 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
44 			float *pos, size[2];
45 			if (!(*i).w->IsVisible()) {
46 				if ((*i).w->IsMouseOver() == true)
47 					(*i).w->OnMouseLeave();
48 				continue;
49 			}
50 			int evmask = (*i).w->GetEventMask();
51 			if (!(evmask & Widget::EVENT_MOUSEMOTION)) continue;
52 
53 			pos = (*i).pos;
54 			(*i).w->GetSize(size);
55 
56 			if ((x >= pos[0]) && (x < pos[0] + size[0]) &&
57 				(y >= pos[1]) && (y < pos[1] + size[1])) {
58 				e->x = x - pos[0];
59 				e->y = y - pos[1];
60 				if ((*i).w->IsMouseOver() == false) {
61 					(*i).w->OnMouseEnter();
62 				}
63 				bool alive = (*i).w->OnMouseMotion(e);
64 				if (!alive) return false;
65 			} else {
66 				if ((*i).w->IsMouseOver() == true)
67 					(*i).w->OnMouseLeave();
68 			}
69 		}
70 		return true;
71 	}
72 
HandleMouseEvent(MouseButtonEvent * e)73 	bool Container::HandleMouseEvent(MouseButtonEvent *e)
74 	{
75 		float x = e->x;
76 		float y = e->y;
77 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
78 			float *pos, size[2];
79 			if (!(*i).w->IsVisible()) continue;
80 			if (!(*i).w->GetEnabled()) continue;
81 			int evmask = (*i).w->GetEventMask();
82 			if (e->isdown) {
83 				if (!(evmask & Widget::EVENT_MOUSEDOWN)) continue;
84 			} else {
85 				if (!(evmask & Widget::EVENT_MOUSEUP)) continue;
86 			}
87 			pos = (*i).pos;
88 			(*i).w->GetSize(size);
89 
90 			if ((x >= pos[0]) && (x < pos[0] + size[0]) &&
91 				(y >= pos[1]) && (y < pos[1] + size[1])) {
92 				e->x = x - pos[0];
93 				e->y = y - pos[1];
94 				bool alive;
95 				if (e->isdown) {
96 					alive = (*i).w->OnMouseDown(e);
97 				} else {
98 					alive = (*i).w->OnMouseUp(e);
99 				}
100 				if (!alive) return false;
101 			}
102 		}
103 		onMouseButtonEvent.emit(e);
104 		return true;
105 	}
106 
DeleteAllChildren()107 	void Container::DeleteAllChildren()
108 	{
109 		PROFILE_SCOPED()
110 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
111 			delete (*i).w;
112 		}
113 		m_children.clear();
114 	}
115 
RemoveAllChildren()116 	void Container::RemoveAllChildren()
117 	{
118 		PROFILE_SCOPED()
119 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
120 			i->w->SetParent(0);
121 		}
122 		m_children.clear();
123 	}
124 
PrependChild(Widget * child,float x,float y)125 	void Container::PrependChild(Widget *child, float x, float y)
126 	{
127 		PROFILE_SCOPED()
128 		assert(child->GetParent() == 0);
129 		assert(FindChild(child) == m_children.end());
130 
131 		widget_pos wp;
132 		wp.w = child;
133 		wp.pos[0] = x;
134 		wp.pos[1] = y;
135 		wp.flags = 0;
136 		child->SetParent(this);
137 		m_children.push_front(wp);
138 	}
139 
AppendChild(Widget * child,float x,float y)140 	void Container::AppendChild(Widget *child, float x, float y)
141 	{
142 		PROFILE_SCOPED()
143 		assert(child->GetParent() == 0);
144 		assert(FindChild(child) == m_children.end());
145 
146 		widget_pos wp;
147 		wp.w = child;
148 		wp.pos[0] = x;
149 		wp.pos[1] = y;
150 		wp.flags = 0;
151 		child->SetParent(this);
152 		m_children.push_back(wp);
153 	}
154 
MoveChild(Widget * child,float x,float y)155 	void Container::MoveChild(Widget *child, float x, float y)
156 	{
157 		PROFILE_SCOPED()
158 		WidgetList::iterator it = FindChild(child);
159 		if (it != m_children.end()) {
160 			it->pos[0] = x;
161 			it->pos[1] = y;
162 		}
163 	}
164 
RemoveChild(Widget * child)165 	void Container::RemoveChild(Widget *child)
166 	{
167 		PROFILE_SCOPED()
168 		WidgetList::iterator it = FindChild(child);
169 		if (it != m_children.end()) {
170 			it->w->SetParent(0);
171 			m_children.erase(it);
172 		}
173 	}
174 
FindChild(const Widget * w) const175 	Container::WidgetList::const_iterator Container::FindChild(const Widget *w) const
176 	{
177 		PROFILE_SCOPED()
178 		for (WidgetList::const_iterator i = m_children.begin(); i != m_children.end(); ++i)
179 			if (i->w == w) return i;
180 		return m_children.end();
181 	}
182 
FindChild(const Widget * w)183 	Container::WidgetList::iterator Container::FindChild(const Widget *w)
184 	{
185 		PROFILE_SCOPED()
186 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i)
187 			if (i->w == w) return i;
188 		return m_children.end();
189 	}
190 
Draw()191 	void Container::Draw()
192 	{
193 		PROFILE_SCOPED()
194 
195 		Graphics::Renderer *r = Gui::Screen::GetRenderer();
196 		r->SetRenderState(Gui::Screen::alphaBlendState);
197 
198 		float size[2];
199 		GetSize(size);
200 		if (!m_transparent) {
201 			PROFILE_SCOPED_RAW("Container::Draw - !m_transparent")
202 			if (!m_rect) {
203 				m_rect.reset(new Graphics::Drawables::Rect(Screen::GetRenderer(), vector2f(0.f), vector2f(size[0], size[0]), m_bgcol, Screen::alphaBlendState, false));
204 			} else {
205 				m_rect->Update(vector2f(0.f), vector2f(size[0], size[1]), m_bgcol);
206 			}
207 			m_rect->Draw(Screen::GetRenderer());
208 		}
209 
210 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
211 			PROFILE_SCOPED_RAW("Container::Draw - Child Loop")
212 			if (!(*i).w->IsVisible())
213 				continue;
214 
215 			matrix4x4f modelView = r->GetTransform();
216 			modelView.Translate((*i).pos[0], (*i).pos[1], 0);
217 			Graphics::Renderer::MatrixTicket ticket(r, modelView);
218 			(*i).w->Draw();
219 		}
220 	}
221 
OnMouseDown(MouseButtonEvent * e)222 	bool Container::OnMouseDown(MouseButtonEvent *e)
223 	{
224 		return HandleMouseEvent(e);
225 	}
226 
OnMouseUp(MouseButtonEvent * e)227 	bool Container::OnMouseUp(MouseButtonEvent *e)
228 	{
229 		return HandleMouseEvent(e);
230 	}
231 
ShowChildren()232 	void Container::ShowChildren()
233 	{
234 		PROFILE_SCOPED()
235 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
236 			(*i).w->Show();
237 		}
238 	}
239 
HideChildren()240 	void Container::HideChildren()
241 	{
242 		PROFILE_SCOPED()
243 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
244 			(*i).w->Hide();
245 		}
246 	}
247 
GetChildPosition(const Widget * child,float outPos[2]) const248 	void Container::GetChildPosition(const Widget *child, float outPos[2]) const
249 	{
250 		WidgetList::const_iterator it = FindChild(child);
251 		assert(it != m_children.end());
252 		outPos[0] = it->pos[0];
253 		outPos[1] = it->pos[1];
254 	}
255 
Show()256 	void Container::Show()
257 	{
258 		PROFILE_SCOPED()
259 		Widget::Show();
260 		if (IsVisible()) {
261 			ResizeRequest();
262 		}
263 	}
264 
ShowAll()265 	void Container::ShowAll()
266 	{
267 		PROFILE_SCOPED()
268 		for (WidgetList::iterator i = m_children.begin(), itEnd = m_children.end(); i != itEnd; ++i) {
269 			(*i).w->ShowAll();
270 		}
271 		Show();
272 	}
273 
HideAll()274 	void Container::HideAll()
275 	{
276 		PROFILE_SCOPED()
277 		HideChildren();
278 		Hide();
279 	}
280 
SetBgColor(const Color & col)281 	void Container::SetBgColor(const Color &col)
282 	{
283 		m_bgcol = col;
284 	}
285 
286 } // namespace Gui
287