pcsx2/common/include/Utilities/wxGuiTools.h

809 lines
23 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// ----------------------------------------------------------------------------
// wxGuiTools.h
//
// This file is meant to contain utility classes for users of the wxWidgets library.
// All classes in this file are dependent on wxBase and wxCore libraries! Meaning
// you will have to use wxCore header files and link against wxCore (GUI) to build
// them. For tools which require only wxBase, see wxBaseTools.h
// ----------------------------------------------------------------------------
#if wxUSE_GUI
#include "Dependencies.h"
#include <memory>
#include <stack>
#include <wx/wx.h>
class pxStaticText;
class pxStaticHeading;
class pxCheckBox;
class wxSpinCtrl;
namespace pxSizerFlags
{
static int StdPadding = 4;
extern wxSizerFlags StdSpace();
extern wxSizerFlags StdCenter();
extern wxSizerFlags StdExpand();
extern wxSizerFlags TopLevelBox();
extern wxSizerFlags SubGroup();
extern wxSizerFlags StdButton();
extern wxSizerFlags Checkbox();
extern void SetBestPadding();
};
#define wxSF wxSizerFlags()
// --------------------------------------------------------------------------------------
// pxAlignment / pxStretchType
// --------------------------------------------------------------------------------------
// These are full blown class types instead of enumerations because wxSizerFlags has an
// implicit conversion from integer (silly design flaw creating more work for me!)
//
struct pxAlignmentType
{
enum {
Centre,
Center = Centre,
Middle,
Left,
Right,
Top,
Bottom
};
int intval;
wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const;
wxSizerFlags operator&(const wxSizerFlags &_flgs) const
{
return Apply(_flgs);
}
wxSizerFlags Expand() const
{
return Apply().Expand();
}
wxSizerFlags Border(int dir, int padding) const
{
return Apply().Border(dir, padding);
}
wxSizerFlags Proportion() const
{
return Apply().Proportion(intval);
}
operator wxSizerFlags() const
{
return Apply();
}
};
struct pxStretchType
{
enum {
Shrink,
Expand,
Shaped,
ReserveHidden,
FixedMinimum
};
int intval;
wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const;
wxSizerFlags operator&(const wxSizerFlags &_flgs) const
{
return Apply(_flgs);
}
wxSizerFlags Border(int dir, int padding) const
{
return Apply().Border(dir, padding);
}
wxSizerFlags Proportion() const
{
return Apply().Proportion(intval);
}
operator wxSizerFlags() const
{
return Apply();
}
};
static __fi wxSizerFlags pxProportion(int prop)
{
return wxSizerFlags(prop);
}
static __fi wxSizerFlags pxBorder(int dir = wxALL, int pad = pxSizerFlags::StdPadding)
{
return wxSizerFlags().Border(dir, pad);
}
class pxStretchSpacer
{
public:
int proportion;
pxStretchSpacer(int prop = 0)
{
proportion = prop;
}
};
extern const pxAlignmentType
pxCentre, // Horizontal centered alignment
pxCenter,
pxMiddle, // vertical centered alignment
pxAlignLeft,
pxAlignRight,
pxAlignTop,
pxAlignBottom;
extern const pxStretchType
pxShrink,
pxExpand,
pxShaped,
pxReserveHidden,
pxFixedMinimum;
// --------------------------------------------------------------------------------------
// pxWindowAndFlags
// --------------------------------------------------------------------------------------
// This struct is a go-between for combining windows and sizer flags in "neat" fashion.
// To create the struct, use the | operator, like so:
//
// myPanel | wxSizerFlags().Expand()
//
// Implementation Note: This struct is a template as it allows us to use a special
// version of the += operator that retains the type information of the window, in case
// the window implements its own += overloads (one example is pxStaticText). Without the
// template, the type of the window would only be known as "wxWindow" when it's added to the
// sizer, and would thus fail to invoke the correct operator overload.
//
template <typename WinType>
struct pxWindowAndFlags
{
WinType *window;
wxSizerFlags flags;
};
extern wxSizerFlags operator&(const wxSizerFlags &_flgs, const wxSizerFlags &_flgs2);
template <typename WinType, typename = typename std::enable_if<std::is_base_of<wxObject, WinType>::value>::type>
pxWindowAndFlags<WinType> operator|(WinType *_win, const wxSizerFlags &_flgs)
{
pxWindowAndFlags<WinType> result = {_win, _flgs};
return result;
}
template <typename WinType, typename = typename std::enable_if<std::is_base_of<wxObject, WinType>::value>::type>
pxWindowAndFlags<WinType> operator|(WinType &_win, const wxSizerFlags &_flgs)
{
pxWindowAndFlags<WinType> result = {&_win, _flgs};
return result;
}
// --------------------------------------------------------------------------------------
// wxSizer Operator += .. a wxSizer.Add() Substitute
// --------------------------------------------------------------------------------------
// This set of operators is the *recommended* method for adding windows to sizers, not just
// because it's a lot prettier but also because it allows controls like pxStaticText to over-
// ride default sizerflags behavior.
//
// += operator works on either sizers, wxDialogs or wxPanels. In the latter case, the window
// is added to the dialog/panel's toplevel sizer (wxPanel.GetSizer() is used). If the panel
// has no sizer set via SetSizer(), an assertion is generated.
//
extern void operator+=(wxSizer &target, wxWindow *src);
extern void operator+=(wxSizer &target, wxSizer *src);
extern void operator+=(wxSizer &target, wxWindow &src);
extern void operator+=(wxSizer &target, wxSizer &src);
extern void operator+=(wxSizer *target, wxWindow &src);
extern void operator+=(wxSizer *target, wxSizer &src);
extern void operator+=(wxSizer &target, int spacer);
extern void operator+=(wxWindow &target, int spacer);
extern void operator+=(wxSizer &target, const pxStretchSpacer &spacer);
extern void operator+=(wxWindow &target, const pxStretchSpacer &spacer);
// ----------------------------------------------------------------------------
// Important: This template is needed in order to retain window type information and
// invoke the proper overloaded version of += (which is used by pxStaticText and other
// classes to perform special actions when added to sizers).
template <typename WinType>
void operator+=(wxWindow &target, WinType *src)
{
if (!pxAssert(target.GetSizer() != NULL))
return;
*target.GetSizer() += src;
}
template <typename WinType>
void operator+=(wxWindow &target, WinType &src)
{
if (!pxAssert(target.GetSizer() != NULL))
return;
*target.GetSizer() += src;
}
template <typename WinType>
void operator+=(wxWindow &target, const pxWindowAndFlags<WinType> &src)
{
if (!pxAssert(target.GetSizer() != NULL))
return;
*target.GetSizer() += src;
}
template <typename WinType>
void operator+=(wxSizer &target, const pxWindowAndFlags<WinType> &src)
{
target.Add(src.window, src.flags);
}
wxDECLARE_EVENT(pxEvt_OnDialogCreated, wxCommandEvent);
// --------------------------------------------------------------------------------------
// pxDialogCreationFlags
// --------------------------------------------------------------------------------------
class pxDialogCreationFlags
{
public:
wxSize MinimumSize;
wxOrientation BoxSizerOrient;
bool isResizable;
bool hasContextHelp;
bool hasCaption;
bool hasMinimizeBox;
bool hasMaximizeBox;
bool hasSystemMenu;
bool hasCloseBox;
public:
virtual ~pxDialogCreationFlags() = default;
pxDialogCreationFlags()
{
MinimumSize = wxDefaultSize;
BoxSizerOrient = wxVERTICAL;
isResizable = false;
hasContextHelp = false;
hasCloseBox = true;
hasSystemMenu = true;
hasMinimizeBox = false;
hasMaximizeBox = false;
hasCaption = true;
}
pxDialogCreationFlags &SetSizerOrient(wxOrientation orient)
{
BoxSizerOrient = orient;
return *this;
}
pxDialogCreationFlags &SetResize(bool enable = true)
{
isResizable = enable;
return *this;
}
pxDialogCreationFlags &SetMinimize(bool enable = true)
{
hasMinimizeBox = enable;
return *this;
}
pxDialogCreationFlags &SetMaximize(bool enable = true)
{
hasMaximizeBox = enable;
return *this;
}
// NOTE: Enabling system menu on dialogs usually doesn't work, and might cause
// other unwanted behavior, such as a missing close button.
pxDialogCreationFlags &SetSystemMenu(bool enable = true)
{
hasSystemMenu = enable;
return *this;
}
pxDialogCreationFlags &SetCaption(bool enable = true)
{
hasCaption = enable;
return *this;
}
pxDialogCreationFlags &SetCloseBox(bool enable = true)
{
hasCloseBox = enable;
return *this;
}
pxDialogCreationFlags SetContextHelp(bool enabled = true)
{
hasContextHelp = enabled;
return *this;
}
pxDialogCreationFlags &SetMinWidth(int width)
{
if (width > MinimumSize.x)
MinimumSize.SetWidth(width);
return *this;
}
pxDialogCreationFlags &SetMinHeight(int height)
{
if (height > MinimumSize.y)
MinimumSize.SetHeight(height);
return *this;
}
pxDialogCreationFlags &SetMinSize(const wxSize &size)
{
return SetMinWidth(size.x).SetMinHeight(size.y);
}
pxDialogCreationFlags Horizontal() const
{
return pxDialogCreationFlags(*this).SetSizerOrient(wxHORIZONTAL);
}
pxDialogCreationFlags Vertical() const
{
return pxDialogCreationFlags(*this).SetSizerOrient(wxVERTICAL);
}
pxDialogCreationFlags NoSizer() const
{
return pxDialogCreationFlags(*this).SetSizerOrient((wxOrientation)0);
}
pxDialogCreationFlags Resize(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetResize(enable);
}
pxDialogCreationFlags Minimize(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetMinimize(enable);
}
pxDialogCreationFlags Maximize(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetMaximize(enable);
}
// NOTE: Enabling system menu on dialogs usually doesn't work, and might cause
// other unwanted behavior, such as a missing close button.
pxDialogCreationFlags SystemMenu(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetSystemMenu(false);
}
pxDialogCreationFlags Caption(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetCaption(enable);
}
pxDialogCreationFlags CloseBox(bool enable = true) const
{
return pxDialogCreationFlags(*this).SetCloseBox(enable);
}
pxDialogCreationFlags NoResize() const
{
return pxDialogCreationFlags(*this).SetResize(false);
}
pxDialogCreationFlags NoMinimize() const
{
return pxDialogCreationFlags(*this).SetMinimize(false);
}
pxDialogCreationFlags NoMaximize() const
{
return pxDialogCreationFlags(*this).SetMaximize(false);
}
pxDialogCreationFlags NoSystemMenu() const
{
return pxDialogCreationFlags(*this).SetSystemMenu(false);
}
pxDialogCreationFlags NoCaption() const
{
return pxDialogCreationFlags(*this).SetCaption(false);
}
pxDialogCreationFlags NoCloseBox() const
{
return pxDialogCreationFlags(*this).SetCloseBox(false);
}
pxDialogCreationFlags MinWidth(int width) const
{
return pxDialogCreationFlags(*this).SetMinWidth(width);
}
pxDialogCreationFlags MinHeight(int height) const
{
return pxDialogCreationFlags(*this).SetMinHeight(height);
}
pxDialogCreationFlags MinSize(const wxSize &size) const
{
return pxDialogCreationFlags(*this).SetMinSize(size);
}
pxDialogCreationFlags MinSize(int width, int height) const
{
return pxDialogCreationFlags(*this).SetMinWidth(width).SetMinHeight(height);
}
int GetWxWindowFlags() const
{
int retval = 0;
if (isResizable)
retval |= wxRESIZE_BORDER;
if (hasCaption)
retval |= wxCAPTION;
if (hasMaximizeBox)
retval |= wxMAXIMIZE_BOX;
if (hasMinimizeBox)
retval |= wxMINIMIZE_BOX;
if (hasSystemMenu)
retval |= wxSYSTEM_MENU;
if (hasCloseBox)
retval |= wxCLOSE_BOX;
return retval;
}
};
// --------------------------------------------------------------------------------------
// wxDialogWithHelpers
// --------------------------------------------------------------------------------------
class wxDialogWithHelpers : public wxDialog
{
typedef wxDialog _parent;
DECLARE_DYNAMIC_CLASS_NO_COPY(wxDialogWithHelpers)
protected:
bool m_hasContextHelp;
wxBoxSizer *m_extraButtonSizer;
wxRect m_CreatedRect;
public:
wxDialogWithHelpers();
wxDialogWithHelpers(wxWindow *parent, const wxString &title, const pxDialogCreationFlags &cflags = pxDialogCreationFlags());
virtual ~wxDialogWithHelpers() = default;
void Init(const pxDialogCreationFlags &cflags);
void AddOkCancel(wxSizer &sizer, bool hasApply = false);
void AddOkCancel(wxSizer *sizer = NULL, bool hasApply = false);
void RememberPosition();
virtual void SmartCenterFit();
virtual int ShowModal();
virtual bool Show(bool show = true);
// Must return the same thing as GetNameStatic; a name ideal for use in uniquely
// identifying dialogs. (this version is the 'instance' version, which is called
// by BaseConfigurationDialog to assign the wxWidgets dialog name, and for saving
// screenshots to disk)
virtual wxString GetDialogName() const;
virtual wxStaticText &Label(const wxString &label);
virtual pxStaticText &Text(const wxString &label);
virtual pxStaticText &Heading(const wxString &label);
wxDialogWithHelpers &SetMinWidth(int newWidth);
wxDialogWithHelpers &SetMinHeight(int newHeight);
int GetCharHeight() const;
protected:
void OnDialogCreated(wxCommandEvent &evt);
void OnOkCancel(wxCommandEvent &evt);
bool ShouldPreventAppExit() const { return false; }
void DoAutoCenter();
};
// --------------------------------------------------------------------------------------
// wxPanelWithHelpers
// --------------------------------------------------------------------------------------
// Overview of Helpers provided by this class:
// * Simpler constructors that have wxID, position, and size parameters removed (We never
// use them in pcsx2)
//
// * Automatic 'primary box sizer' creation, assigned via SetSizer() -- use GetSizer()
// to retrieve it, or use the "*this += window;" syntax to add windows directly to it.
//
// * Built-in support for StaticBoxes (aka groupboxes). Create one at construction with
// a wxString label, or add one "after the fact" using AddFrame.
//
// * Propagates IdealWidth settings from parenting wxPanelWithHelpers classes, and auto-
// matically adjusts the width based on the sizer type (groupsizers get truncated to
// account for borders).
//
class wxPanelWithHelpers : public wxPanel
{
DECLARE_DYNAMIC_CLASS_NO_COPY(wxPanelWithHelpers)
public:
wxPanelWithHelpers(wxWindow *parent, wxOrientation orient, const wxString &staticBoxLabel);
wxPanelWithHelpers(wxWindow *parent, wxOrientation orient);
wxPanelWithHelpers(wxWindow *parent, const wxPoint &pos, const wxSize &size = wxDefaultSize);
explicit wxPanelWithHelpers(wxWindow *parent = NULL);
wxPanelWithHelpers *AddFrame(const wxString &label, wxOrientation orient = wxVERTICAL);
wxStaticText &Label(const wxString &label);
pxStaticText &Text(const wxString &label);
pxStaticText &Heading(const wxString &label);
virtual wxPanelWithHelpers &SetMinWidth(int newWidth);
protected:
void Init();
};
// --------------------------------------------------------------------------------------
// pxTextWrapperBase
// --------------------------------------------------------------------------------------
// this class is used to wrap the text on word boundary: wrapping is done by calling
// OnStartLine() and OnOutputLine() functions. This class by itself can be used as a
// line counting tool, but produces no formatted text output.
//
// [class "borrowed" from wxWidgets private code, made public, and renamed to avoid possible
// conflicts with future editions of wxWidgets which might make it public. Why this isn't
// publicly available already in wxBase I'll never know-- air]
//
class pxTextWrapperBase
{
protected:
bool m_eol;
int m_linecount;
wxString m_indent;
public:
virtual ~pxTextWrapperBase() = default;
pxTextWrapperBase(const wxString &indent = wxEmptyString)
: m_indent(indent)
{
m_eol = false;
m_linecount = 0;
}
// win is used for getting the font, text is the text to wrap, width is the
// max line width or -1 to disable wrapping
pxTextWrapperBase &Wrap(const wxWindow &win, const wxString &text, int widthMax);
int GetLineCount() const
{
return m_linecount;
}
protected:
// line may be empty
virtual void OnOutputLine(const wxString &line) {}
// called at the start of every new line (except the very first one)
virtual void OnNewLine() {}
void DoOutputLine(const wxString &line);
bool IsStartOfNewLine();
};
// --------------------------------------------------------------------------------------
// pxTextWrapper
// --------------------------------------------------------------------------------------
// This class extends pxTextWrapperBase and adds the ability to retrieve the formatted
// result of word wrapping.
//
class pxTextWrapper : public pxTextWrapperBase
{
typedef pxTextWrapperBase _parent;
protected:
wxString m_text;
public:
pxTextWrapper(const wxString &wrapPrefix = wxEmptyString)
: pxTextWrapperBase(wrapPrefix)
{
}
virtual ~pxTextWrapper() = default;
const wxString &GetResult() const
{
return m_text;
}
pxTextWrapper &Wrap(const wxWindow &win, const wxString &text, int widthMax);
pxTextWrapper &Wrap(const wxWindow *win, const wxString &text, int widthMax);
protected:
void OnOutputLine(const wxString &line);
void OnNewLine();
};
// --------------------------------------------------------------------------------------
// pxWindowTextWriter
// --------------------------------------------------------------------------------------
class pxWindowTextWriter
{
protected:
wxDC &m_dc;
wxAlignment m_align;
wxPoint m_curpos;
int m_leading;
virtual void _DoWriteLn(const wxChar *msg);
void _DoWriteLn(const wxString msg);
void _DoWrite(const wxChar *msg);
public:
pxWindowTextWriter(wxDC &dc);
virtual ~pxWindowTextWriter() = default;
virtual void OnFontChanged();
pxWindowTextWriter &WriteLn();
pxWindowTextWriter &FormatLn(const wxChar *fmt, ...);
pxWindowTextWriter &WriteLn(const wxChar *fmt);
pxWindowTextWriter &SetFont(const wxFont &font);
pxWindowTextWriter &Align(const wxAlignment &align);
pxWindowTextWriter &WriteLn(const wxString fmt);
pxWindowTextWriter &SetLeading(int lead)
{
m_leading = lead;
return *this;
}
pxWindowTextWriter &SetWeight(wxFontWeight weight);
pxWindowTextWriter &SetStyle(wxFontStyle style);
pxWindowTextWriter &Normal();
pxWindowTextWriter &Bold()
{
return SetWeight(wxFONTWEIGHT_BOLD);
}
pxWindowTextWriter &Italic()
{
return SetStyle(wxFONTSTYLE_ITALIC);
}
pxWindowTextWriter &SetPos(const wxPoint &pos);
pxWindowTextWriter &MovePos(const wxSize &delta);
pxWindowTextWriter &SetPos(int xpos, int ypos)
{
return SetPos(wxPoint(xpos, ypos));
}
pxWindowTextWriter &MovePos(int xdelta, int ydelta)
{
return MovePos(wxSize(xdelta, ydelta));
}
pxWindowTextWriter &SetY(int ypos);
pxWindowTextWriter &MoveY(int ydelta);
};
// --------------------------------------------------------------------------------------
// MoreStockCursors
// --------------------------------------------------------------------------------------
// Because (inexplicably) the ArrowWait cursor isn't in wxWidgets stock list.
//
class MoreStockCursors
{
protected:
std::unique_ptr<wxCursor> m_arrowWait;
public:
MoreStockCursors() {}
virtual ~MoreStockCursors() = default;
const wxCursor &GetArrowWait();
};
enum BusyCursorType {
Cursor_NotBusy,
Cursor_KindaBusy,
Cursor_ReallyBusy,
};
extern MoreStockCursors StockCursors;
// --------------------------------------------------------------------------------------
// ScopedBusyCursor
// --------------------------------------------------------------------------------------
// ... because wxWidgets wxBusyCursor doesn't really do proper nesting (doesn't let me
// override a partially-busy cursor with a really busy one)
class ScopedBusyCursor
{
protected:
static std::stack<BusyCursorType> m_cursorStack;
static BusyCursorType m_defBusyType;
public:
ScopedBusyCursor(BusyCursorType busytype);
virtual ~ScopedBusyCursor();
static void SetDefault(BusyCursorType busytype);
static void SetManualBusyCursor(BusyCursorType busytype);
};
// --------------------------------------------------------------------------------------
// pxFitToDigits
// --------------------------------------------------------------------------------------
// Fits a given text or spinner control to the number of digits requested, since by default
// they're usually way over-sized.
extern void pxFitToDigits(wxWindow *win, int digits);
extern void pxFitToDigits(wxSpinCtrl *win, int digits);
extern wxTextCtrl *CreateNumericalTextCtrl(wxWindow *parent, int digits, long flags = wxTE_RIGHT);
//////////////////////////////////////////////////////////////////////////////////////////////
extern bool pxDialogExists(const wxString &name);
extern bool pxIsValidWindowPosition(const wxWindow &window, const wxPoint &windowPos);
extern wxRect wxGetDisplayArea();
extern wxString pxGetAppName();
extern int pxGetCharHeight(const wxWindow *wind, int rows = 1);
extern int pxGetCharHeight(const wxWindow &wind, int rows = 1);
extern void pxSetToolTip(wxWindow *wind, const wxString &src);
extern void pxSetToolTip(wxWindow &wind, const wxString &src);
extern wxFont pxGetFixedFont(int ptsize = 8, wxFontWeight weight = wxFONTWEIGHT_NORMAL, bool underline = false);
extern pxDialogCreationFlags pxDialogFlags();
#endif