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 "Gui.h"
5 
6 #include "graphics/Renderer.h"
7 #include "text/TextSupport.h"
8 #include "vector3.h" // for projection
9 
10 namespace Gui {
11 
12 	bool Screen::initted = false;
13 	int Screen::width;
14 	int Screen::height;
15 	int Screen::realWidth;
16 	int Screen::realHeight;
17 	float Screen::invRealWidth;
18 	float Screen::invRealHeight;
19 	float Screen::fontScale[2];
20 	std::list<Widget *> Screen::kbshortcut_widgets;
21 	Gui::Fixed *Screen::baseContainer;
22 	Gui::Widget *Screen::focusedWidget;
23 	matrix4x4f Screen::modelMatrix;
24 	matrix4x4f Screen::projMatrix;
25 	Graphics::Viewport Screen::viewport;
26 
27 	FontCache Screen::s_fontCache;
28 	std::stack<RefCountedPtr<Text::TextureFont>> Screen::s_fontStack;
29 	RefCountedPtr<Text::TextureFont> Screen::s_defaultFont;
30 
31 	Graphics::Renderer *Screen::s_renderer;
32 	Graphics::RenderState *Screen::alphaBlendState = nullptr;
33 	Graphics::Material *Screen::flatColorMaterial = nullptr;
34 
Init(Graphics::Renderer * renderer,int real_width,int real_height,int ui_width,int ui_height)35 	void Screen::Init(Graphics::Renderer *renderer, int real_width, int real_height, int ui_width, int ui_height)
36 	{
37 		s_renderer = renderer;
38 
39 		Screen::width = ui_width;
40 		Screen::height = ui_height;
41 		Screen::realWidth = real_width;
42 		Screen::realHeight = real_height;
43 		Screen::invRealWidth = 1.0f / real_width;
44 		Screen::invRealHeight = 1.0f / real_height;
45 		Screen::initted = true;
46 		// Why? because although our font textures get bigger with screen
47 		// resolution, our Gui Ortho projection is still 800x600 so vertex
48 		// coords must be scaled.
49 		Screen::fontScale[0] = ui_width / float(real_width);
50 		Screen::fontScale[1] = ui_height / float(real_height);
51 		s_defaultFont = s_fontCache.GetTextureFont("GuiFont");
52 		PushFont(s_defaultFont);
53 		Screen::baseContainer = new Gui::Fixed();
54 		Screen::baseContainer->SetSize(float(Screen::width), float(Screen::height));
55 		Screen::baseContainer->Show();
56 
57 		Graphics::RenderStateDesc rsd;
58 		rsd.blendMode = Graphics::BLEND_ALPHA;
59 		rsd.depthWrite = false;
60 		alphaBlendState = renderer->CreateRenderState(rsd);
61 
62 		Graphics::MaterialDescriptor mdesc;
63 		flatColorMaterial = renderer->CreateMaterial(mdesc);
64 	}
65 
Uninit()66 	void Screen::Uninit()
67 	{
68 		Screen::baseContainer->RemoveAllChildren(); // children deleted elsewhere?
69 		delete Screen::baseContainer;
70 		delete flatColorMaterial;
71 	}
72 
73 	static sigc::connection _focusedWidgetOnDelete;
74 
OnDeleteFocusedWidget()75 	void Screen::OnDeleteFocusedWidget()
76 	{
77 		_focusedWidgetOnDelete.disconnect();
78 		focusedWidget = 0;
79 	}
80 
SetFocused(Widget * w,bool enableKeyRepeat)81 	void Screen::SetFocused(Widget *w, bool enableKeyRepeat)
82 	{
83 		ClearFocus();
84 		_focusedWidgetOnDelete = w->onDelete.connect(sigc::ptr_fun(&Screen::OnDeleteFocusedWidget));
85 		focusedWidget = w;
86 	}
87 
ClearFocus()88 	void Screen::ClearFocus()
89 	{
90 		if (!focusedWidget) return;
91 		_focusedWidgetOnDelete.disconnect();
92 		focusedWidget = 0;
93 	}
94 
Project(const vector3d & in,vector3d & out)95 	bool Screen::Project(const vector3d &in, vector3d &out)
96 	{
97 		PROFILE_SCOPED()
98 		// implements gluProject (see the OpenGL documentation or the Mesa implementation of gluProject)
99 		const vector3d vcam = matrix4x4d(modelMatrix) * in;
100 		const double w = vcam.z * projMatrix[11] + projMatrix[15];
101 
102 		// convert view coordinates -> homogeneous coordinates -> NDC
103 		// perspective divide is applied last (left-to-right associativity)
104 		const vector3d vNDC = matrix4x4d(projMatrix) * vcam / w;
105 
106 		// convert -1..1 NDC to 0..1 viewport coordinates
107 		const vector3d vVP = {
108 			vNDC.x * 0.5 + 0.5,
109 			vNDC.y * 0.5 + 0.5,
110 			-vNDC.z // undo reverse-Z coordinate flip
111 		};
112 
113 		// viewport coord * size + position
114 		out.x = vVP.x * viewport.w + viewport.x;
115 		out.y = vVP.y * viewport.h + viewport.y;
116 		out.z = vVP.z;
117 
118 		// map to pixels
119 		out.x = out.x * width * invRealWidth;
120 		out.y = GetHeight() - out.y * height * invRealHeight;
121 		return true;
122 	}
123 
EnterOrtho()124 	void Screen::EnterOrtho()
125 	{
126 		PROFILE_SCOPED()
127 
128 		Graphics::Renderer *r = GetRenderer();
129 
130 		modelMatrix = r->GetTransform();
131 		projMatrix = r->GetProjection();
132 
133 		viewport = r->GetViewport();
134 		r->SetOrthographicProjection(0, width, height, 0, 0, 1);
135 		r->SetTransform(matrix4x4f::Identity());
136 	}
137 
LeaveOrtho()138 	void Screen::LeaveOrtho()
139 	{
140 		PROFILE_SCOPED()
141 
142 		Graphics::Renderer *r = GetRenderer();
143 
144 		r->SetProjection(projMatrix);
145 		r->SetTransform(modelMatrix);
146 	}
147 
Draw()148 	void Screen::Draw()
149 	{
150 		PROFILE_SCOPED()
151 		assert(Screen::initted);
152 		EnterOrtho();
153 		baseContainer->Draw();
154 		LeaveOrtho();
155 	}
156 
IsBaseWidget(const Widget * w)157 	bool Screen::IsBaseWidget(const Widget *w)
158 	{
159 		PROFILE_SCOPED()
160 		return w == static_cast<const Widget *>(baseContainer);
161 	}
162 
AddBaseWidget(Widget * w,int x,int y)163 	void Screen::AddBaseWidget(Widget *w, int x, int y)
164 	{
165 		baseContainer->Add(w, float(x), float(y));
166 	}
167 
RemoveBaseWidget(Widget * w)168 	void Screen::RemoveBaseWidget(Widget *w)
169 	{
170 		baseContainer->Remove(w);
171 	}
172 
SDLEventCoordToScreenCoord(int sdlev_x,int sdlev_y,float * x,float * y)173 	void Screen::SDLEventCoordToScreenCoord(int sdlev_x, int sdlev_y, float *x, float *y)
174 	{
175 		*y = sdlev_y * height * invRealHeight;
176 		*x = sdlev_x * width * invRealWidth;
177 	}
178 
OnMouseMotion(SDL_MouseMotionEvent * e)179 	void Screen::OnMouseMotion(SDL_MouseMotionEvent *e)
180 	{
181 		MouseMotionEvent ev;
182 		float x, y;
183 		Screen::SDLEventCoordToScreenCoord(e->x, e->y, &x, &y);
184 		ev.screenX = ev.x = x;
185 		ev.screenY = ev.y = y;
186 		baseContainer->OnMouseMotion(&ev);
187 		ev.screenX = ev.x = x;
188 		ev.screenY = ev.y = y;
189 		RawEvents::onMouseMotion.emit(&ev);
190 	}
191 
OnClick(SDL_MouseButtonEvent * e)192 	void Screen::OnClick(SDL_MouseButtonEvent *e)
193 	{
194 		MouseButtonEvent ev;
195 		float x, y;
196 		Screen::SDLEventCoordToScreenCoord(e->x, e->y, &x, &y);
197 		ev.button = e->button;
198 		ev.isdown = (e->type == SDL_MOUSEBUTTONDOWN);
199 		ev.screenX = ev.x = x;
200 		ev.screenY = ev.y = y;
201 		if (ev.isdown) {
202 			baseContainer->OnMouseDown(&ev);
203 			RawEvents::onMouseDown.emit(&ev);
204 		} else {
205 			baseContainer->OnMouseUp(&ev);
206 			RawEvents::onMouseUp.emit(&ev);
207 		}
208 	}
209 
OnKeyDown(const SDL_Keysym * sym)210 	void Screen::OnKeyDown(const SDL_Keysym *sym)
211 	{
212 		if (focusedWidget) {
213 			bool accepted = focusedWidget->OnKeyDown(sym);
214 			// don't check shortcuts if the focused widget accepted the key-press
215 			if (accepted)
216 				return;
217 		}
218 		for (std::list<Widget *>::iterator i = kbshortcut_widgets.begin(); i != kbshortcut_widgets.end(); ++i) {
219 			if (!(*i)->IsVisible()) continue;
220 			if (!(*i)->GetEnabled()) continue;
221 			(*i)->OnPreShortcut(sym);
222 		}
223 	}
224 
OnKeyUp(const SDL_Keysym * sym)225 	void Screen::OnKeyUp(const SDL_Keysym *sym)
226 	{
227 	}
228 
OnTextInput(const SDL_TextInputEvent * e)229 	void Screen::OnTextInput(const SDL_TextInputEvent *e)
230 	{
231 		if (!focusedWidget) return;
232 		Uint32 unicode;
233 		Text::utf8_decode_char(&unicode, e->text);
234 		focusedWidget->OnTextInput(unicode);
235 	}
236 
GetFontHeight(Text::TextureFont * font)237 	float Screen::GetFontHeight(Text::TextureFont *font)
238 	{
239 		if (!font) font = GetFont().Get();
240 
241 		return font->GetHeight() * fontScale[1];
242 	}
243 
GetFontDescender(Text::TextureFont * font)244 	float Screen::GetFontDescender(Text::TextureFont *font)
245 	{
246 		if (!font) font = GetFont().Get();
247 
248 		return font->GetDescender() * fontScale[1];
249 	}
250 
MeasureString(const std::string & s,float & w,float & h,Text::TextureFont * font)251 	void Screen::MeasureString(const std::string &s, float &w, float &h, Text::TextureFont *font)
252 	{
253 		if (!font) font = GetFont().Get();
254 		assert(font);
255 
256 		font->MeasureString(s.c_str(), w, h);
257 		w *= fontScale[0];
258 		h *= fontScale[1];
259 	}
260 
MeasureCharacterPos(const std::string & s,int charIndex,float & x,float & y,Text::TextureFont * font)261 	void Screen::MeasureCharacterPos(const std::string &s, int charIndex, float &x, float &y, Text::TextureFont *font)
262 	{
263 		assert((charIndex >= 0) && (charIndex <= int(s.size())));
264 
265 		if (!font) font = GetFont().Get();
266 		assert(font);
267 
268 		font->MeasureCharacterPos(s.c_str(), charIndex, x, y);
269 		x *= fontScale[0];
270 		y *= fontScale[1];
271 	}
272 
PickCharacterInString(const std::string & s,float x,float y,Text::TextureFont * font)273 	int Screen::PickCharacterInString(const std::string &s, float x, float y, Text::TextureFont *font)
274 	{
275 		if (!font) font = GetFont().Get();
276 		assert(font);
277 
278 		x /= fontScale[0];
279 		y /= fontScale[1];
280 
281 		return font->PickCharacter(s.c_str(), x, y);
282 	}
283 
RenderStringBuffer(RefCountedPtr<Graphics::VertexBuffer> vb,const std::string & s,float xoff,float yoff,const Color & color,Text::TextureFont * font)284 	void Screen::RenderStringBuffer(RefCountedPtr<Graphics::VertexBuffer> vb, const std::string &s, float xoff, float yoff, const Color &color, Text::TextureFont *font)
285 	{
286 		PROFILE_SCOPED()
287 		if (!font) font = GetFont().Get();
288 
289 		Graphics::Renderer *r = Gui::Screen::GetRenderer();
290 
291 		const matrix4x4f &modelMatrix_ = r->GetTransform();
292 		Graphics::Renderer::MatrixTicket ticket(r);
293 
294 		const float x = modelMatrix_[12] + xoff;
295 		const float y = modelMatrix_[13] + yoff;
296 
297 		matrix4x4f modelView = matrix4x4f::Identity();
298 		modelView.Translate(floor(x / Screen::fontScale[0]) * Screen::fontScale[0], floor(y / Screen::fontScale[1]) * Screen::fontScale[1], 0);
299 		modelView.Scale(Screen::fontScale[0], Screen::fontScale[1], 1);
300 		r->SetTransform(modelView);
301 
302 		// temporary, owned by the font
303 		Graphics::VertexBuffer *pVB = font->GetCachedVertexBuffer(s);
304 		if (pVB) {
305 			// found the buffer
306 			font->RenderBuffer(pVB, color);
307 		} else {
308 			Graphics::VertexArray va(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0);
309 			font->PopulateString(va, s.c_str(), 0, 0, color);
310 
311 			if (va.GetNumVerts() > 0) {
312 				if (!vb.Valid() || vb->GetCapacity() < va.GetNumVerts()) {
313 					vb.Reset(font->CreateVertexBuffer(va, s, true));
314 				}
315 
316 				vb->Populate(va);
317 
318 				font->RenderBuffer(vb.Get(), color);
319 			}
320 		}
321 	}
322 
RenderMarkupBuffer(RefCountedPtr<Graphics::VertexBuffer> vb,const std::string & s,const Color & color,Text::TextureFont * font)323 	void Screen::RenderMarkupBuffer(RefCountedPtr<Graphics::VertexBuffer> vb, const std::string &s, const Color &color, Text::TextureFont *font)
324 	{
325 		PROFILE_SCOPED()
326 		if (!font) font = GetFont().Get();
327 
328 		Graphics::Renderer *r = Gui::Screen::GetRenderer();
329 
330 		const matrix4x4f &modelMatrix_ = r->GetTransform();
331 		Graphics::Renderer::MatrixTicket ticket(r);
332 
333 		const float x = modelMatrix_[12];
334 		const float y = modelMatrix_[13];
335 
336 		matrix4x4f modelView = matrix4x4f::Identity();
337 		modelView.Translate(floor(x / Screen::fontScale[0]) * Screen::fontScale[0], floor(y / Screen::fontScale[1]) * Screen::fontScale[1], 0);
338 		modelView.Scale(Screen::fontScale[0], Screen::fontScale[1], 1);
339 		r->SetTransform(modelView);
340 
341 		// temporary, owned by the font
342 		Graphics::VertexBuffer *pVB = font->GetCachedVertexBuffer(s);
343 		if (pVB) {
344 			// found the buffer
345 			font->RenderBuffer(pVB, color);
346 		} else {
347 			Graphics::VertexArray va(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0);
348 			font->PopulateMarkup(va, s.c_str(), 0, 0, color);
349 
350 			if (va.GetNumVerts() > 0) {
351 				if (!vb.Valid() || vb->GetCapacity() < va.GetNumVerts()) {
352 					vb.Reset(font->CreateVertexBuffer(va, s, true));
353 				}
354 
355 				vb->Populate(va);
356 
357 				font->RenderBuffer(vb.Get());
358 			}
359 		}
360 	}
361 
AddShortcutWidget(Widget * w)362 	void Screen::AddShortcutWidget(Widget *w)
363 	{
364 		kbshortcut_widgets.push_back(w);
365 	}
366 
RemoveShortcutWidget(Widget * w)367 	void Screen::RemoveShortcutWidget(Widget *w)
368 	{
369 		kbshortcut_widgets.remove(w);
370 	}
371 
372 } // namespace Gui
373