Files
Flips/arlib/gui/window.h
2017-01-05 03:51:38 +01:00

791 lines
34 KiB
C++

#pragma once
#include "../global.h"
#include "../string.h"
#include <string.h>
class window;
class windowmenu_menu;
class widget_base;
#if defined(ARGUI_WINDOWS)
#define ARGUI_MANUAL_LAYOUT
#endif
//This must be called before calling any other window_*, before creating any interface that does any I/O, before calling anything from
// the malloc() family, and before using argc/argv; basically, before doing anything else. It should be the first thing main() does.
//It does the following actions, in whatever order makes sense:
//- Initialize the window system, if needed; on failure, terminates the process
//- Read off any arguments it recognizes (if any), and delete them; for example, it takes care of --display and a few others on GTK+
//- Convert argv[0] to the standard path format, if needed (hi Windows)
void window_init(int * argc, char * * argv[]);
//Returns false if the window system couldn't be initialized, rather than terminating.
bool window_try_init(int * argc, char * * argv[]);
//On Windows, attaches stdout/stderr to the console of the launching process. On Linux, does nothing.
//On both, returns whether the process is currently in a terminal. Returns true if I/O is redirected.
//Returns whether the process was launched from a console.
//If yes, calling window_console_attach will connect stdout/stderr to something (stdin not guaranteed to work).
bool window_console_avail();
bool window_console_attach(); // Returns whether it worked.
//On Windows, the program is assumed portable, so it returns the program directory.
//On Linux, the program is assumed installed, so it returns the user's config directory.
string window_config_path();
//window toolkit is not choosable at runtime
//It is safe to interact with this window while inside its callbacks, with the exception that you may not free it.
//You may also not use window_run_*().
class window {
public:
//Marks the window as a popup dialog. This makes it act differently in some ways.
//For example, poking Escape will close it, and it may or may not get a thinner window border.
//Must be called before the first call to set_visible(). Can't be undone, and can't be called multiple times.
virtual void set_is_dialog() = 0;
//Sets which window created this one. This can, for example, center it on top of the parent.
//Should generally be combined with set_is_popup.
//Must be called before the first call to set_visible(). Can't be undone, and can't be called multiple times.
virtual void set_parent(window* parent) = 0;
//Blocks interacting with other windows in the program while this one is visible.
//It is undefined behaviour to have two modal windows visible simultaneously.
virtual void set_modal(bool modal) = 0;
//newwidth and newheight are the content size, excluding menus/toolbars/etc.
//If there is any widget whose size is unknown inside, then the sizes may only be used in resize(), and for relative measurements.
//It is allowed to call resize() on unresizable windows, but changing the size of
// any contents (changing a label text, for example) will resize it to minimum.
//If resizable, the resize callback is called after the window is resized and everything is set to the new sizes.
virtual void resize(unsigned int width, unsigned int height) = 0;
virtual void set_resizable(bool resizable, function<void(unsigned int newwidth, unsigned int newheight)> onresize) = 0;
virtual void get_pos(int * x, int * y) = 0;
//Do not move to position (0,0) - it puts the window border outside the screen.
virtual void set_pos(int x, int y) = 0;
virtual void set_onmove(function<void(int x, int y)> onmove) = 0;
virtual void set_title(const char * title) = 0;
//The callback tells whether the close request should be honored; true for close, false for keep.
//The window is only hidden, not deleted; you can use show() again later.
//It is safe to free this structure from within this callback; if you do this, return true for close.
virtual void set_onclose(function<bool()> onclose) = 0;
//Appends a menu bar to the top of the window. If the window has a menu already, it's replaced. NULL removes the menu.
//But there's no real reason to replace it. Just change it.
//Must be created by windowmenu_menu::create_top.
virtual void set_menu(windowmenu_menu* menu) = 0;
////Creates a status bar at the bottom of the window. It is undefined what happens if numslots equals or exceeds 32.
////align is how each string is aligned; 0 means touch the left side, 1 means centered, 2 means touch the right side.
////dividerpos is in 240ths of the window size. Values 0 and 240, as well as
//// a divider position to the left of the previous one, yield undefined behaviour.
////dividerpos[numslots-1] is ignored; the status bar always covers the entire width of the window.
////It is implementation defined whether the previous status bar strings remain, or if you must use statusbar_set again.
////It is implementation defined whether dividers will be drawn. However, it is guaranteed
//// that the implementation will look like the rest of the operating system, as far as that's feasible.
////It is implementation defined what exactly happens if a string is too
//// long to fit; however, it is guaranteed to show as much as it can.
////To remove the status bar, set numslots to 0.
//virtual void statusbar_create(int numslots, const int * align, const int * dividerpos) = 0;
////Sets a string on the status bar. The index is zero-based. All strings are initially blank.
//virtual void statusbar_set(int slot, const char * text) = 0;
//This replaces the contents of a window.
virtual void replace_contents(widget_base* contents) = 0;
//Setting a window visible while it already is will do nothing.
virtual void set_visible(bool visible) = 0;
virtual bool is_visible() = 0;
//Call only after making the window visible.
virtual void focus() = 0;
//If the menu is active, the window is considered not active.
//If the menu doesn't exist, it is considered not active.
//If the window is hidden, results are undefined.
virtual bool is_active() = 0;
virtual bool menu_active() = 0;
//This will also remove the window from the screen, if it's visible.
virtual ~window() = 0;
//Only usable by the implementation, don't call them yourself. Not guaranteed to be implemented at all.
//Returns a native handle to the window.
virtual uintptr_t _get_handle() { return 0; }
//Recomputes the window content layout.
//If return value is false, the reflow will be done later and the old sizes are still present.
virtual bool _reflow() { return false; };
};
inline window::~window(){}
window* window_create(widget_base* contents);
//Each widget is owned by the layout or window it's put in (layouts can own more layouts). Deleting the parent deletes the children.
//Each widget has a few shared base functions that can be called without knowing what
// type of widget this is. However, they should all be seen as implementation details.
//It is undefined behaviour to query a widget's state before it's placed inside a window.
//Any pointers given during widget creation must be valid until the widget is placed inside a window.
//Most functions return the object it's called on, so object state can be set while creating the object.
class widget_base : nocopy {
public:
#ifdef ARGUI_MANUAL_LAYOUT
//measure() returns no value, but sets the width and height. The sizes are undefined if the last
// function call on the widget was not measure(); widgets may choose to update their sizes in
// response to anything that resizes them, and leave measure() blank.
//If multiple widgets want the space equally much, they get equal fractions, in addition to their base demand.
//If a widget gets extra space and doesn't want it, it should add some padding in any direction.
//The widget should, if needed by the window manager, forward all plausible events to its parent window,
// unless the widget wants the events. (For example, a button will want mouse events, but not file drop events.)
//The window handles passed around are implementation defined.
//The return value from init() is the number of child windows involved, from the window manager's point of view.
virtual unsigned int init(window * parent, uintptr_t parenthandle) = 0;
virtual void measure() = 0;
unsigned int width;
unsigned int height;
virtual void place(void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height) = 0;
//this one acts roughly like Q_OBJECT
#define WIDGET_BASE \
unsigned int init(window * parent, uintptr_t parenthandle); \
void measure(); \
void place(void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
#else
void * widget;
#define WIDGET_BASE /* */
#endif
//The priorities mean:
//0 - Widget has been assigned a certain size; it must get exactly that. (Canvas, viewport)
//1 - Widget wants a specific size; will only grudgingly accept more. (Most of them)
//2 - Widget has orders to consume extra space if there's any left over and nothing really wants it. (Padding)
//3 - Widget will look better if given extra space. (Textbox, listbox)
//4 - Widget is ordered to be resizable. (Canvas, viewport)
uint8_t widthprio;
uint8_t heightprio;
virtual ~widget_base() {};
};
class widget_padding : public widget_base { WIDGET_BASE
public:
widget_padding(bool vertical);
~widget_padding();
public:
struct impl;
impl * m;
};
static inline widget_padding* widget_create_padding_horz() { return new widget_padding(false); }
static inline widget_padding* widget_create_padding_vert() { return new widget_padding(true); }
class widget_layout : public widget_base { WIDGET_BASE
protected:
void construct(unsigned int numchildren, widget_base* * children,
unsigned int totwidth, unsigned int * widths, bool uniformwidths,
unsigned int totheight, unsigned int * heights, bool uniformheights);
widget_layout() {}
public:
//The lists are terminated with a NULL. It shouldn't be empty.
widget_layout(bool vertical, bool uniform, widget_base* firstchild, ...);
#define widget_create_layout_horz(...) (new widget_layout(false, false, __VA_ARGS__))
#define widget_create_layout_vert(...) (new widget_layout(true, false, __VA_ARGS__))
//This one allows some widgets to take up multiple boxes of the grid. They're still stored row by
// row, except that there is no entry for slots that are already used.
//It is undefined behaviour if a widget does not fit where it belongs, if it overlaps another widget,
// or if it's size 0 in either direction.
widget_layout(unsigned int totwidth, unsigned int totheight, bool uniformwidths, bool uniformheights,
unsigned int firstwidth, unsigned int firstheight, widget_base* firstchild, ...);
#define widget_create_layout(...) (new widget_layout(__VA_ARGS__))
//In this one, the widths/heights arrays can be NULL, which is treated as being filled with 1s.
//But if you want that, you should probably use the grid constructor instead. (Though it's useful for the constructors themselves.)
widget_layout(unsigned int numchildren, widget_base* * children,
unsigned int totwidth, unsigned int * widths, bool uniformwidths,
unsigned int totheight, unsigned int * heights, bool uniformheights)
{
construct(numchildren, children, totwidth, widths, uniformwidths, totheight, heights, uniformheights);
}
~widget_layout();
public:
struct impl;
impl * m;
};
//The widgets are stored row by row. There is no NULL terminator, because the size is known from the arguments already.
//Uniform sizes mean that every row has the same height, and every column has the same width.
widget_layout* widget_create_layout_grid(unsigned int width, unsigned int height, bool uniformsizes, widget_base* firstchild, ...);
class widget_label : public widget_base { WIDGET_BASE
public:
widget_label(const char * text = "");
~widget_label();
//Disabling a label does nothing, but may change how it looks.
//Useful it if it's attached to another widget, and this widget is disabled.
widget_label* set_enabled(bool enable);
widget_label* set_text(const char * text);
widget_label* set_ellipsize(bool ellipsize);//Defaults to false.
//Alignment 0 means touch the left side, 1 means centered, 2 means touch the right side. Defaults to left.
widget_label* set_alignment(int align);
public:
struct impl;
impl * m;
};
#define widget_create_label(...) new widget_label(__VA_ARGS__)
class widget_button : public widget_base { WIDGET_BASE
public:
widget_button(const char * text = "");
~widget_button();
widget_button* set_enabled(bool enable);
widget_button* set_text(const char * text);
widget_button* set_onclick(function<void()> onclick);
public:
struct impl;
impl * m;
};
#define widget_create_button(...) (new widget_button(__VA_ARGS__))
class widget_checkbox : public widget_base { WIDGET_BASE
public:
widget_checkbox(const char * text = "");
~widget_checkbox();
widget_checkbox* set_enabled(bool enable);
widget_checkbox* set_text(const char * text);
bool get_state();
widget_checkbox* set_state(bool checked);
widget_checkbox* set_onclick(function<void(bool checked)> onclick);
public:
struct impl;
impl * m;
};
#define widget_create_checkbox(...) (new widget_checkbox(__VA_ARGS__))
class widget_radio : public widget_base { WIDGET_BASE
public:
widget_radio(const char * text = "");
~widget_radio();
widget_radio* set_enabled(bool enable);
widget_radio* set_text(const char * text);
//The button this function is called on becomes the group leader. The leader must be the first in the group.
//It is undefined behaviour to attempt to redefine a group.
//It is undefined behaviour to set the onclick handler, or set or get the state, for anything except the group leader.
//The window may not be shown before grouping them.
widget_radio* group(unsigned int numitems, widget_radio* * group);
//Returns which one is active. The group leader is 0.
unsigned int get_state();
//State values are the same as get_state().
widget_radio* set_state(unsigned int state);
//Called whenever the state changes. It is allowed to set the state in response to this.
//It is undefined whether the callback can fire for the previously active state, for example due to clicking the button twice.
//Must be set only for the group leader.
widget_radio* set_onclick(function<void(unsigned int state)> onclick);
public:
struct impl;
impl * m;
};
#define widget_create_radio(...) (new widget_radio(__VA_ARGS__))
//This one wraps them in a horizontal or vertical layout, and groups them.
//It's just a convenience; you can create them and group them manually and get the same results.
//The first one will expect a set of radio buttons. The second will expect a set of radio button texts, and will put the group leader in the pointer.
widget_layout* widget_create_radio_group(bool vertical, widget_radio* leader, ...);
widget_layout* widget_create_radio_group(bool vertical, widget_radio* * leader, const char * firsttext, ...);
#define widget_create_radio_group_horz(...) widget_create_radio_group(false, __VA_ARGS__)
#define widget_create_radio_group_vert(...) widget_create_radio_group(true, __VA_ARGS__)
class widget_textbox : public widget_base { WIDGET_BASE
public:
widget_textbox();
~widget_textbox();
widget_textbox* set_enabled(bool enable);
widget_textbox* focus();
//The return value is guaranteed valid until the next call to any function
// on this object, or the next window_run[_iter], whichever comes first.
const char * get_text();
widget_textbox* set_text(const char * text);
//If the length is 0, it's unlimited.
widget_textbox* set_length(unsigned int maxlen);
//How many instances of the letter 'X' should fit in the textbox without scrolling. Defaults to 5.
widget_textbox* set_width(unsigned int xs);
//Highlights the widget as containing invalid data. Can paint the background red, focus it, and various other stuff.
//The invalidity highlight is removed as soon as the contents are changed, but may be restored on the onchange event.
//Making a widget invalid and disabled simultaneously is undefined behaviour.
widget_textbox* set_invalid(bool invalid);
//Called whenever the text changes.
//Note that it is not guaranteed to fire only if the text has changed; it may, for example,
// fire if the user selects an E and types another E on top. Or for no reason at all.
//Also note that 'text' is invalidated under the same conditions as get_text is.
widget_textbox* set_onchange(function<void(const char * text)> onchange);
//Called if the user hits Enter while this widget is focused. [TODO: Doesn't that activate the default button instead?]
widget_textbox* set_onactivate(function<void(const char * text)> onactivate);
public:
struct impl;
impl * m;
};
#define widget_create_textbox(...) (new widget_textbox(__VA_ARGS__))
////A canvas is a simple image. It's easy to work with, but performance is poor and it can't vsync, so it shouldn't be used for video.
//class widget_canvas : public widget_base { WIDGET_BASE
//public:
// widget_canvas(unsigned int width, unsigned int height);
// ~widget_canvas();
// //can't disable this
//
// widget_canvas* resize(unsigned int width, unsigned int height);
// uint32_t * (*draw_begin)();
// void draw_end();
//
// //TODO
// ////Whether to hide the cursor while it's on top of this widget.
// ////The mouse won't instantly hide; if it's moving, it will be visible. The exact details are up to the implementation,
// //// but it will be similar to "the mouse is visible if it has moved within the last 1000 milliseconds".
// //widget_canvas* set_hide_cursor(bool hide);
// //
// ////This must be called before the window is shown, and only exactly once.
// ////All given filenames are invalidated once the callback returns.
// //widget_canvas* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
//
//public:
// struct impl;
// impl * m;
//};
//#define widget_create_canvas(width, height) (new widget_canvas(width, height))
//A viewport fills the same purpose as a canvas, but the tradeoffs go the opposite way.
class widget_viewport : public widget_base { WIDGET_BASE
public:
widget_viewport(unsigned int width, unsigned int height);
~widget_viewport();
widget_viewport* resize(unsigned int width, unsigned int height);
//There's no single way to render high-performance graphics (especially 3d), so a separate video driver is required.
//Many video drivers (especially OpenGL-based ones) can't render to arbitrary windows, but must create their own windows;
// therefore, this widget doesn't create its own window, but expects to be given one by the video driver.
// The driver retains ownership and is expected to delete it.
//The driver's created window should be a child of this one:
uintptr_t get_parent();
//As this widget is resizable, it needs a way to report size changes.
//This is done via this function. Call it and the widget will move and resize the window to whereever this widget is located.
//If the widget changes size, this will be reported to onresize(). Guaranteed to only be called if actually changed.
//This callback will be called if the widget is altered by resize(). The rest of Arlib avoids calling callbacks for API-sourced calls,
// but the video driver isn't the one who called resize().
//The driver must return the new window after the resize. This may be the same as the old one. If different,
// the driver is responsible for destroying the old one.
//If the program wants to destroy the video driver, it must call set_contents(0, NULL, NULL) before doing so.
//If the driver doesn't need the resize callback, it may return 0. However, set_contents() is still required.
//ondestroy is called whenever the viewport is destroyed, if the viewport isn't disconnected first.
void set_child(uintptr_t windowhandle, function<void(unsigned int width, unsigned int height)> onresize, function<void()> ondestroy);
//TODO
////See documentation of canvas for these.
//widget_viewport* set_hide_cursor(bool hide);
//widget_viewport* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
//TODO
////Keycodes are from libretro; 0 if unknown. Scancodes are implementation defined and always present.
//widget_viewport* set_kb_callback(function<void(unsigned int keycode, unsigned int scancode)> keyboard_cb);
public:
struct impl;
impl * m;
};
#define widget_create_viewport(width, height) (new widget_viewport(width, height))
class widget_listbox_virtual : public widget_base { WIDGET_BASE
private:
void construct(unsigned int numcolumns, const char * * columns);
public:
widget_listbox_virtual(unsigned int numcolumns, const char * * columns) { construct(numcolumns, columns); }
template<typename... Args>
widget_listbox_virtual(Args... cols)
{
const char * cols_up[] = { cols... };
construct(sizeof...(cols), cols_up);
}
~widget_listbox_virtual();
widget_listbox_virtual* set_enabled(bool enable);
//Column -1 is the checkboxes, if they exist; NULL for unchecked, non-NULL (not necessarily a valid pointer) for checked.
//The search callback should return the row ID closest to 'start' in the given direction where the first column starts with 'str'.
//If 'start' itself starts with 'prefix', it should be returned.
//If there is none in that direction, loop around. If still no match, return (size_t)-1.
//It's optional, but recommended for better performance.
//(GTK+ is stupid and doesn't let me use it.)
widget_listbox_virtual* set_contents(function<const char * (size_t row, int column)> get_cell,
function<size_t(const char * prefix, size_t start, bool up)> search);
//On Windows, the limit is 100 million; if more than that, it puts in 0.
// Probably because it's a nice round number, and the listbox row height (19) times 100 million is fairly close to 2^31.
//On GTK+, it's 100000; it's slow on huge lists, since it allocates memory for each row,
// even when using gtk_tree_view_set_fixed_height_mode and similar.
static size_t get_max_rows();
//If more than get_max_rows(), it's capped to that.
widget_listbox_virtual* set_num_rows(size_t rows);
//Call this after changing anything. It's fine to change multiple rows before calling this.
widget_listbox_virtual* refresh();
//If the active row changes, set_focus_change will fire. However, onactivate will likely not.
//The exact conditions under which a listbox entry is activated is platform dependent, but double
// click and Enter are likely. It is guaranteed to be possible.
//Returns (size_t)-1 if no row is active.
size_t get_active_row();
widget_listbox_virtual* set_on_focus_change(function<void(size_t row)> onchange);
widget_listbox_virtual* set_onactivate(function<void(size_t row)> onactivate);
//This is the size on the screen. The height is how many items show up below the header;
// the widths are the longest string that should comfortably fit (or the column header, whichever is wider).
//0 in height means "use some sensible default"; NULL in widths means "only check the column".
//'expand' is which column should get all extra size, if the widget is given more space than it asked for; -1 for even distribution.
//Defaults to 10, column headers, and -1.
//TODO: do this on Windows.
widget_listbox_virtual* set_size(unsigned int height, const char * const * widths, int expand);
//It is implementation defined how the checkboxes are represented. They can be prepended to the
// first column, on a column of their own, or something weirder. The position relative to the
// other columns is not guaranteed, though it is likely to be the leftmost column.
//The toggle callback does not contain the current nor former state; the user is expected to keep track of that.
widget_listbox_virtual* add_checkboxes(function<void(size_t row)> ontoggle);
//TODO (maybe): make columns editable; for windows, it's LVN_BEGINLABELEDIT
public:
struct impl;
impl * m;
};
#define widget_create_listbox_virtual(...) (new widget_listbox_virtual(__VA_ARGS__))
//Easier to use than the virtual listbox, but slower. Should be preferred for most usecases.
class widget_listbox : public widget_listbox_virtual
{
size_t numcols;
size_t numrows;
char * * * cells; // [row][col] is a string
const char * get_cell(size_t row, int column) { return cells[row][column]; }
void init(int numcols)
{
this->numcols = numcols;
this->numrows = 0;
this->cells = NULL;
widget_listbox_virtual::set_contents(bind_this(&widget_listbox::get_cell), NULL);
}
public:
widget_listbox(unsigned int numcolumns, const char * * columns) : widget_listbox_virtual(numcolumns, columns)
{
init(numcolumns);
}
template<typename... Args>
widget_listbox(Args... cols) : widget_listbox_virtual(cols...)
{
init(sizeof...(cols));
}
~widget_listbox()
{
for (size_t row=0;row<numrows;row++)
{
for (size_t col=0;col<numcols;col++)
{
free(cells[row][col]);
}
free(cells[row]);
}
free(cells);
}
size_t rows() { return numrows; }
widget_listbox* add_row(const char * const * cols) { insert_row(numrows, cols); return this; }
widget_listbox* add_row(const char * * cols) { add_row((const char * const *)cols); return this; }
template<typename... Args>
widget_listbox* add_row(Args... cols)
{
const char * cols_up[] = { cols... };
add_row(cols_up);
return this;
}
widget_listbox* insert_row(size_t before, const char * const * cols)
{
cells = realloc(cells, sizeof(char**)*(numrows+1));
memmove(cells+before+1, cells+before, sizeof(char**)*(numrows-before));
numrows++;
cells[before] = malloc(sizeof(char*)*numcols);
for (size_t col=0;col<numcols;col++)
{
cells[before][col] = strdup(cols[col]);
}
widget_listbox_virtual::set_num_rows(numrows);
return this;
}
widget_listbox* insert_row(size_t before, const char * * cols) { insert_row(before, (const char * const *)cols); return this; }
template<typename... Args>
widget_listbox* insert_row(size_t before, Args... cols)
{
const char * cols_up[] = { cols... };
insert_row(before, cols_up);
return this;
}
widget_listbox* delete_row(size_t row)
{
for (size_t col=0;col<numcols;col++)
{
free(cells[row][col]);
}
numrows--;
memmove(cells+row, cells+row+1, sizeof(char**)*(numrows-row));
widget_listbox_virtual::set_num_rows(numrows);
return this;
}
widget_listbox* replace_row(size_t row, const char * const * cols)
{
for (size_t col=0;col<numcols;col++)
{
free(cells[row][col]);
cells[row][col] = strdup(cols[col]);
}
widget_listbox_virtual::refresh();
return this;
}
widget_listbox* replace_row(size_t row, const char ** cols) { replace_row(row, (const char * const *)cols); return this; }
template<typename... Args>
widget_listbox* replace_row(size_t row, Args... cols)
{
const char * cols_up[] = { cols... };
replace_row(row, cols_up);
return this;
}
widget_listbox* replace_cell(size_t row, size_t col, const char * text)
{
free(cells[row][col]);
cells[row][col] = strdup(text);
widget_listbox_virtual::refresh();
return this;
}
private:
//Calling these will screw up the internal state. Yes, you can do it, but it'll break if you try.
widget_listbox_virtual* set_contents(function<const char * (size_t row, int column)> get_cell,
function<size_t(const char * prefix, size_t start, bool up)> search);
widget_listbox_virtual* set_num_rows(size_t rows);
widget_listbox_virtual* refresh(size_t row);
widget_listbox_virtual* add_checkboxes(function<void(size_t row)> ontoggle);
};
#define widget_create_listbox(...) (new widget_listbox(__VA_ARGS__))
//A decorative frame around a widget, to group them together. The widget can be a layout (and probably should, otherwise you're adding a box to a single widget).
class widget_frame : public widget_base { WIDGET_BASE
public:
widget_frame(const char * text, widget_base* contents);
~widget_frame();
//can't disable this (well okay, we can, but it's pointless to disable a widget that does nothing)
widget_frame* set_text(const char * text);
public:
struct impl;
impl * m;
};
#define widget_create_frame(...) (new widget_frame(__VA_ARGS__))
//The only thing that may be done with a menu item before it's in a window, is passing it into another menu constructor.
//The text pointers, if any, must be also be valid until added to the parent.
//Destruction is not allowed.
class windowmenu : nocopy {
public:
virtual ~windowmenu() = 0;
public:
struct impl;
impl * m;
};
inline windowmenu::~windowmenu(){}
class windowmenu_item : public windowmenu {
public:
void set_enabled(bool enable);
static windowmenu_item* create(const char * text, function<void(void)> onactivate);
~windowmenu_item();
public:
struct impl;
impl * mu;
};
class windowmenu_check : public windowmenu {
public:
void set_enabled(bool enable);
bool get_checked();
void set_checked(bool checked);
static windowmenu_check* create(const char * text, function<void(bool checked)> onactivate);
~windowmenu_check();
public:
struct impl;
impl * mu;
};
class windowmenu_radio : public windowmenu {
public:
void set_enabled(bool enable);
//If the new state is out of range, it's undefined behaviour.
unsigned int get_state();
void set_state(unsigned int state);
static windowmenu_radio* create(function<void(unsigned int state)> onactivate, const char * firsttext, ...);
static windowmenu_radio* create(unsigned int numitems, const char * const * texts, function<void(unsigned int state)> onactivate);
~windowmenu_radio();
public:
struct impl;
impl * mu;
};
class windowmenu_separator : public windowmenu {
public:
static windowmenu_separator* create();
~windowmenu_separator();
public:
struct impl;
impl * mu;
};
class windowmenu_menu : public windowmenu {
public:
void insert_child(unsigned int pos, windowmenu* child);
void remove_child(windowmenu* child);
static windowmenu_menu* create(const char * text, windowmenu* firstchild, ...);
static windowmenu_menu* create(const char * text, unsigned int numchildren, windowmenu* const * children);
//This one goes in a window. Anything else only goes in other menues.
static windowmenu_menu* create_top(windowmenu_menu* firstchild, ...);
static windowmenu_menu* create_top(unsigned int numchildren, windowmenu_menu* const * children);
~windowmenu_menu();
public:
struct impl;
impl * mu;
};
//Tells the window manager to handle recent events and fire whatever callbacks are relevant.
//Neither of them are allowed while inside any callback of any kind.
//Some other functions may call these two.
void window_run_iter();//Returns as soon as possible. Use if, for example, you're displaying an animation.
void window_run_wait();//Returns only after doing something. Use while idling. It will return if any
// state (other than the time) has changed or if any callback has fired.
//It may also return due to uninteresting events, as often as it wants;
// however, repeatedly calling it will leave the CPU mostly idle.
////Shows a message box. You can do that by creating a label and some buttons, but it gives inferior results.
////Returns true for OK and Yes, and false for Cancel/No/close window.
////The title may or may not be ignored.
//enum mbox_sev { mb_info, mb_warn, mb_err };
//enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
//bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons);
////Usable for both ROMs and dylibs. If dylib is true, the returned filenames are for the system's
//// dynamic linker; this will disable gvfs-like systems the dynamic linker can't understand, and may
//// hide files not marked executable, if this makes sense. If false, only file_read/etc is guaranteed
//// to work.
////If multiple is true, multiple files may be picked; if not, only one can be picked. Should
//// generally be true for dylibs and false for ROMs, but not guaranteed.
////The parent window will be disabled while the dialog is active.
////Both extensions and return value have the format { "smc", ".sfc", NULL }. Extensions are optional.
////Return value is full paths, zero or more. Duplicates are allowed in both input and output.
////The return value is valid until the next call to window_file_picker() or window_run_*(), whichever comes first.
//const char * const * window_file_picker(window * parent,
// const char * title,
// const char * const * extensions,
// const char * extdescription,
// bool dylib,
// bool multiple);
////Returns the number of microseconds since an undefined start time.
////The start point doesn't change while the program is running, but need not be the same across reboots, nor between two processes.
////It can be program launch, system boot, the Unix epoch, or whatever.
//uint64_t window_get_time();
//Implementation details, don't touch.
void _window_init_inner();
void _window_init_misc();
void _window_init_shell();
uintptr_t _window_notify_inner(void* notification);
uintptr_t _window_get_widget_color(unsigned int type, void* handle, void* draw, void* parent);
//This one can be used if the one calling widget_listbox_virtual->set_contents doesn't provide a search function.
size_t _widget_listbox_search(function<const char *(size_t row, int column)> get_cell, size_t rows,
const char * prefix, size_t start, bool up);
#ifdef ARGUIPROT_X11
//Returns the display and screen we should use.
//The concept of screens only exists on X11, so this should not be used elsewhere.
//Only I/O drivers should have any reason to use this.
struct _XDisplay;
typedef struct _XDisplay Display;
struct window_x11_info {
Display* display;
int screen;
unsigned long root; //The real type is Window aka XID.
};
extern struct window_x11_info window_x11;
#endif
//TODO: If porting to Qt, use https://woboq.com/blog/verdigris-qt-without-moc.html
//Windows resources are bad enough, but at least they have a reason to exist -
// they have to be available without executing the program. moc has no such excuse.