summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/ui/menubar.h
blob: 84903ef5a83a823cc9469daba18b3e5cc0da3702 (plain) (blame)
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/***************************************************************************

    menubar.h

    Internal MAME menu bar for the user interface.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#pragma once

#ifndef __UI_MENUBAR_H__
#define __UI_MENUBAR_H__

#include "render.h"


//**************************************************************************
//  MENU BAR
//**************************************************************************

class ui_menubar
{
public:
	ui_menubar(running_machine &machine);
	virtual ~ui_menubar();

	// menu item
	class menu_item
	{
	public:
		menu_item(ui_menubar &menubar, const char *text = NULL, menu_item *parent = NULL, bool is_invokable = false, int shortcut = 0);
		virtual ~menu_item();

		// methods
		menu_item &append(const char *text);
		void append_separator();
		bool is_child_of(menu_item *that) const;
		virtual void invoke();
		void clear_area_recursive();
		menu_item *find_point(float x, float y);
		menu_item &find_child(const char *target);
		const char *shortcut_text();
		float shortcut_text_width();
		void sensible_seq_name(astring &text, const input_seq &seq);

		// template methods; look I tried to use delegate.h but I got humbled...
		template<class _Target> menu_item &append(const char *text, void (_Target::*callback)(), _Target &obj, int shortcut = 0)
		{
			menu_item *child = new invokable_menu_item<_Target>(m_menubar, text, this, callback, obj, shortcut);
			initialize(*child);
			return *child;
		}
		template<class _Target, typename _Arg> menu_item &append(const char *text, void (_Target::*callback)(_Arg), _Target &obj, _Arg arg, int shortcut = 0)
		{
			menu_item *child = new arg_invokable_menu_item<_Target, _Arg>(m_menubar, text, this, callback, obj, arg, shortcut);
			initialize(*child);
			return *child;
		}
		template<class _Target, typename _Arg1, typename _Arg2> menu_item &append(const char *text, void (_Target::*callback)(_Arg1, _Arg2), _Target &obj, _Arg1 arg1, _Arg2 arg2, int shortcut = 0)
		{
			menu_item *child = new arg2_invokable_menu_item<_Target, _Arg1, _Arg2>(m_menubar, text, this, callback, obj, arg1, arg2, shortcut);
			initialize(*child);
			return *child;
		}
		template<class _Target> menu_item &append(const char *text, void (_Target::*set_callback)(bool), bool (_Target::*get_callback)() const, _Target &obj, int shortcut = 0)
		{
			// tailored for a toggle
			bool current_value = ((obj).*(get_callback))();
			menu_item &menu = append(text, set_callback, obj, !current_value, shortcut);
			menu.set_checked(current_value);
			return menu;
		}
		template<class _Target, typename _Arg> menu_item &append(const char *text, void (_Target::*set_callback)(_Arg), _Arg (_Target::*get_callback)() const, _Target &obj, _Arg arg, int shortcut = 0)
		{
			// tailored for a set operation
			_Arg current_value = ((obj).*(get_callback))();
			menu_item &menu = append(text, set_callback, obj, arg, shortcut);
			menu.set_checked(current_value == arg);
			return menu;
		}

		// getters
		bool is_empty() const { return !m_first_child; }
		bool is_invokable() const { return m_is_invokable; }
		bool is_checked() const { return m_is_checked; }
		bool is_enabled() const { return m_is_enabled; }
		bool is_separator() const { return m_is_separator; }
		bool has_children() const { return m_first_child ? true : false; }
		int shortcut() const { return m_shortcut; }
		const astring &text() const { return m_text; }
		menu_item *parent() { return m_parent; }
		menu_item *child() { return m_first_child; }
		menu_item *last_child() { return m_last_child; }
		menu_item *previous() { return m_previous; }
		menu_item *next() { return m_next; }
		menu_item *next_with_shortcut() { return m_next_with_shortcut; }
		bool is_sub_menu() const { return m_parent && m_parent->m_parent; }

		// setters
		void set_area(float x0, float y0, float x1, float y1);
		void clear_area() { set_area(-1, -1, -1, -1); }
		void set_checked(bool checked) { m_is_checked = checked; }
		void set_enabled(bool enabled) { m_is_enabled = enabled; }
		void set_text(const char *text) { m_text.cpy(text); }
		void set_next_with_shortcut(menu_item *item) { m_next_with_shortcut = item; }

	private:
		// private variables
		ui_menubar &	m_menubar;
		astring			m_text;
		int				m_shortcut;
		astring			m_shortcut_text;
		float			m_shortcut_text_width;
		bool			m_is_invokable;
		bool			m_is_checked;
		bool			m_is_enabled;
		bool			m_is_separator;
		menu_item *		m_parent;
		menu_item *		m_first_child;
		menu_item *		m_last_child;
		menu_item *		m_previous;
		menu_item *		m_next;
		menu_item *		m_next_with_shortcut;
		float			m_x0;
		float			m_y0;
		float			m_x1;
		float			m_y1;

		// private methods
		void initialize(menu_item &child);
		menu_item *find_child_internal(const char *target);
	};

	// methods
	virtual void reset();
	virtual void handle(render_container *container);

	// getters
	bool is_visible() const { return m_menubar_visibility != MENUBAR_VISIBILITY_INVISIBLE; }
	bool has_selection() const { return m_selected_item != NULL; }
	menu_item &root_menu() { return m_menus; }

protected:
	// implemented by child classes
	virtual void menubar_build_menus() = 0;
	virtual void menubar_draw_ui_elements() = 0;

	// accessors
	running_machine &machine() { return m_machine; }
	render_container *container() { return m_container; }

private:
	// classes
	template<class _Target>
	class invokable_menu_item : public menu_item
	{
	public:
		invokable_menu_item(ui_menubar &menubar, const char *name, menu_item *parent, void (_Target::*callback)(), _Target &obj, int shortcut)
			: menu_item(menubar, name, parent, true, shortcut), m_callback(callback), m_obj(obj)
		{
		}

		virtual void invoke()	{ ((m_obj).*(m_callback))(); }

	private:
		void (_Target::*m_callback)();
		_Target &m_obj;
	};

	template<class _Target, typename _Arg>
	class arg_invokable_menu_item : public menu_item
	{
	public:
		arg_invokable_menu_item(ui_menubar &menubar, const char *name, menu_item *parent, void (_Target::*callback)(_Arg), _Target &obj, _Arg arg, int shortcut)
			: menu_item(menubar, name, parent, true, shortcut), m_callback(callback), m_obj(obj), m_arg(arg)
		{
		}

		virtual void invoke()	{ ((m_obj).*(m_callback))(m_arg); }

	private:
		void (_Target::*m_callback)(_Arg);
		_Target &m_obj;
		_Arg m_arg;
	};

	template<class _Target, typename _Arg1, typename _Arg2>
	class arg2_invokable_menu_item : public menu_item
	{
	public:
		arg2_invokable_menu_item(ui_menubar &menubar, const char *name, menu_item *parent, void (_Target::*callback)(_Arg1, _Arg2), _Target &obj, _Arg1 arg1, _Arg2 arg2, int shortcut)
			: menu_item(menubar, name, parent, true, shortcut), m_callback(callback), m_obj(obj), m_arg1(arg1), m_arg2(arg2)
		{
		}

		virtual void invoke()	{ ((m_obj).*(m_callback))(m_arg1, m_arg2); }

	private:
		void (_Target::*m_callback)(_Arg1, _Arg2);
		_Target &m_obj;
		_Arg1 m_arg1;
		_Arg2 m_arg2;
	};

	// menubar visibility
	enum menubar_visibility_t
	{
		MENUBAR_VISIBILITY_INVISIBLE,
		MENUBAR_VISIBILITY_TRANSLUCENT,
		MENUBAR_VISIBILITY_VISIBLE
	};

	// instance variables
	running_machine &		m_machine;
	render_container *		m_container;
	menu_item				m_menus;					// the root menu item
	menu_item *				m_shortcuted_menu_items;	// list of menu items with shortcuts
	menu_item *				m_selected_item;			// current selection
	menu_item *				m_active_item;				// active menu item
	bool					m_dragged;					// have we dragged over at least one item?
	float					m_mouse_x, m_mouse_y;
	bool					m_mouse_button;
	float					m_checkmark_width;
	osd_ticks_t				m_last_mouse_move;
	menubar_visibility_t	m_menubar_visibility;

	// selection walking
	bool walk_selection_previous();
	bool walk_selection_next();
	bool walk_selection_child();
	bool walk_selection_parent();
	bool walk_selection_escape();
	bool walk_selection_previous_sub_menu();
	bool walk_selection_next_sub_menu();

	// miscellaneous
	void draw_child_menu(menu_item *menu, float x, float y);
	bool is_child_menu_visible(menu_item *menu) const;
	void draw_menu_item_text(menu_item *mi, float x0, float y0, float x1, float y1, bool decorations, const float *column_widths = NULL);
	bool event_loop();
	bool poll_navigation_keys();
	bool poll_shortcut_keys(bool swallow);
	bool input_pressed_safe(int key);
	void toggle_selection();
	void invoke(menu_item *menu);
	bool find_mouse(float &mouse_x, float &mouse_y, bool &mouse_button);
	menubar_visibility_t get_menubar_visibility();
	rgb_t adjust_color(rgb_t color);
};


#endif /* __UI_MENUBAR_H__ */