File: Panel.h

package info (click to toggle)
endless-sky 0.10.16-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 414,608 kB
  • sloc: cpp: 73,435; python: 893; xml: 666; sh: 271; makefile: 28
file content (189 lines) | stat: -rw-r--r-- 6,326 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* Panel.h
Copyright (c) 2014 by Michael Zahniser

Endless Sky is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.

Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "MouseButton.h"
#include "Rectangle.h"

#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include <SDL2/SDL.h>

class Command;
class Point;
class Sprite;
class TestContext;
class UI;



// Class representing a UI window (full screen or pop-up) which responds to user
// input and can draw itself. Everything displayed in the game is drawn in a
// Panel, and panels can stack on top of each other like "real" UI windows. By
// default, a panel allows the panels under it to show through, but does not
// allow them to receive any events that it does not know how to handle.
class Panel {
public:
	// Draw a sprite repeatedly to make a vertical edge.
	static void DrawEdgeSprite(const Sprite *edgeSprite, int posX);


public:
	// Make the destructor virtual just in case any derived class needs it.
	virtual ~Panel() = default;

	// Move the state of this panel forward one game step.
	virtual void Step();

	// Draw this panel.
	virtual void Draw() = 0;

	// Return true if this is a full-screen panel, so there is no point in
	// drawing any of the panels under it.
	bool IsFullScreen() const noexcept;
	// Return true if, when this panel is on the stack, no events should be
	// passed to any panel under it. By default, all panels do this.
	bool TrapAllEvents() const noexcept;
	// Check if this panel can be "interrupted" to return to the main menu.
	bool IsInterruptible() const noexcept;

	// Clear the list of clickable zones.
	void ClearZones();
	// Add a clickable zone to the panel.
	void AddZone(const Rectangle &rect, const std::function<void()> &fun);
	void AddZone(const Rectangle &rect, SDL_Keycode key);
	// Check if a click at the given coordinates triggers a clickable zone. If
	// so, apply that zone's action and return true.
	bool ZoneClick(const Point &point);

	// Is fast-forward allowed to be on when this panel is on top of the GUI stack?
	virtual bool AllowsFastForward() const noexcept;

	virtual void UpdateTooltipActivation();


protected:
	// Only override the ones you need; the default action is to return false.
	virtual bool KeyDown(SDL_Keycode key, Uint16 mod, const Command &command, bool isNewPress);
	virtual bool Click(int x, int y, MouseButton button, int clicks);
	virtual bool Hover(int x, int y);
	virtual bool Drag(double dx, double dy);
	virtual bool Release(int x, int y, MouseButton button);
	virtual bool Scroll(double dx, double dy);

	virtual void Resize();

	// If a clickable zone is clicked while editing is happening, the panel may
	// need to know to exit editing mode before handling the click.
	virtual void EndEditing() {}

	void SetIsFullScreen(bool set);
	void SetTrapAllEvents(bool set);
	void SetInterruptible(bool set);

	// Dim the background of this panel.
	void DrawBackdrop() const;

	UI *GetUI() const noexcept;
	void SetUI(UI *ui);

	// This is not for overriding, but for calling KeyDown with only one or two
	// arguments. In this form, the command is never set, so you can call this
	// with a key representing a known keyboard shortcut without worrying that a
	// user-defined command key will override it.
	bool DoKey(SDL_Keycode key, Uint16 mod = 0);

	// A lot of different UI elements allow a modifier to change the number of
	// something you are buying, so the shared function is defined here:
	static int Modifier();
	// Display the given help message if it has not yet been shown
	// (or if force is set to true). Return true if the message was displayed.
	bool DoHelp(const std::string &name, bool force = false) const;

	const std::vector<std::shared_ptr<Panel>> &GetChildren();
	// Add a child. Deferred until next frame.
	void AddChild(const std::shared_ptr<Panel> &panel);
	// Remove a child. Deferred until next frame.
	void RemoveChild(const Panel *panel);
	// Handle deferred add/remove child operations.
	void AddOrRemove();

private:
	class Zone : public Rectangle {
	public:
		Zone(const Rectangle &rect, const std::function<void()> &fun) : Rectangle(rect), fun(fun) {}

		void Click() const { fun(); }

	private:
		std::function<void()> fun;
	};

	// The UI class will not directly call the virtual methods, but will call
	// these instead. These methods will recursively allow child panels to
	// handle the event first, before calling the virtual method for the derived
	// class to handle it.
	bool DoKeyDown(SDL_Keycode key, Uint16 mod, const Command &command, bool isNewPress);
	bool DoClick(int x, int y, MouseButton button, int clicks);
	bool DoHover(int x, int y);
	bool DoDrag(double dx, double dy);
	bool DoRelease(int x, int y, MouseButton button);
	bool DoScroll(double dx, double dy);

	void DoDraw();

	void DoResize();

	// Call a method on all the children in reverse order, and then on this
	// object. Recursion stops as soon as any child returns true.
	template<typename...FARGS, typename...ARGS>
	bool EventVisit(bool(Panel::*f)(FARGS ...args), ARGS ...args);


private:
	UI *ui = nullptr;

	bool isFullScreen = false;
	bool trapAllEvents = true;
	bool isInterruptible = true;

	std::list<Zone> zones;

	std::vector<std::shared_ptr<Panel>> children;
	std::vector<std::shared_ptr<Panel>> childrenToAdd;
	std::vector<const Panel *> childrenToRemove;

	friend class UI;
};



template<typename ...FARGS, typename ...ARGS>
bool Panel::EventVisit(bool (Panel::*f)(FARGS ...), ARGS ...args)
{
	// Check if a child panel will consume this event first.
	for(auto it = children.rbegin(); it != children.rend(); ++it)
		if((*it)->EventVisit(f, args...))
			return true;

	// If none of our children handled this event, then it could be for us.
	return (this->*f)(args...);
}