2021SC@SDUSC
Overload代碼分析三:OvWindowing——Dialogs
- 前言
- Dialogs
- 一、FileDialog
- FileDialog.h
- FileDialog.cpp
- 二、MessageBox
- MessageBox.h
- MessageBox.cpp
- 三、OpenFileDialog
- OpenFileDialog.h
- OpenFileDialog.cpp
- 四、SaveFileDialog
- SaveFileDialog.h
- SaveFileDialog.cpp
- Inputs
- 一、EKey.h
- 二、EKeyState.h
- 三、EMouseButton.h
- 四、EMouseButtonState.h
- 五、InputManager
- InputManager.h
- InputManager.cpp
- 一些剩下的檔案
- 一、DeviceSettings.h
- 二、Window.h
- 總結
前言
這是Overload引擎相關的第五篇文章,同時也是OvWindowing分析的第三篇,Overload引擎的Github主頁在這里,
本篇文章主要會介紹OvWindowing中Dialogs檔案夾所包含的h和cpp檔案,同時會把內容比較少,比較簡單Inputs檔案夾內的檔案一并介紹,就算是把OvWindowing中剩余的內容全部講完了,
Dialogs
首先我們來講Dialogs,顧名思義,這個檔案夾下的檔案都是和對話框有關的,我們把h檔案和cpp檔案一對一的講,
一、FileDialog
FileDialog.h
我們先看頭檔案,頭檔案代碼如下:
/**
* Some flags that can be passed to FileDialog instances
*/
enum class EExplorerFlags
{
READONLY = 0x00000001,
OVERWRITEPROMPT = 0x00000002,
HIDEREADONLY = 0x00000004,
NOCHANGEDIR = 0x00000008,
SHOWHELP = 0x00000010,
ENABLEHOOK = 0x00000020,
ENABLETEMPLATE = 0x00000040,
ENABLETEMPLATEHANDLE = 0x00000080,
NOVALIDATE = 0x00000100,
ALLOWMULTISELECT = 0x00000200,
EXTENSIONDIFFERENT = 0x00000400,
PATHMUSTEXIST = 0x00000800,
FILEMUSTEXIST = 0x00001000,
CREATEPROMPT = 0x00002000,
SHAREAWARE = 0x00004000,
NOREADONLYRETURN = 0x00008000,
NOTESTFILECREATE = 0x00010000,
NONETWORKBUTTON = 0x00020000,
NOLONGNAMES = 0x00040000, // force no long names for 4.x modules
EXPLORER = 0x00080000, // new look commdlg
NODEREFERENCELINKS = 0x00100000,
LONGNAMES = 0x00200000, // force long names for 3.x modules
ENABLEINCLUDENOTIFY = 0x00400000, // send include message to callback
ENABLESIZING = 0x00800000,
DONTADDTORECENT = 0x02000000,
FORCESHOWHIDDEN = 0x10000000 // Show All files including System and hidden files
};
inline EExplorerFlags operator~ (EExplorerFlags a) { return (EExplorerFlags)~(int)a; }
inline EExplorerFlags operator| (EExplorerFlags a, EExplorerFlags b) { return (EExplorerFlags)((int)a | (int)b); }
inline EExplorerFlags operator& (EExplorerFlags a, EExplorerFlags b) { return (EExplorerFlags)((int)a & (int)b); }
inline EExplorerFlags operator^ (EExplorerFlags a, EExplorerFlags b) { return (EExplorerFlags)((int)a ^ (int)b); }
inline EExplorerFlags& operator|= (EExplorerFlags& a, EExplorerFlags b) { return (EExplorerFlags&)((int&)a |= (int)b); }
inline EExplorerFlags& operator&= (EExplorerFlags& a, EExplorerFlags b) { return (EExplorerFlags&)((int&)a &= (int)b); }
inline EExplorerFlags& operator^= (EExplorerFlags& a, EExplorerFlags b) { return (EExplorerFlags&)((int&)a ^= (int)b); }
/**
* FileDialog is the base class for any dialog window that asks the user to select/save a file from/to the disk
*/
class FileDialog
{
public:
/**
* Constructor
* @param p_callback
* @param p_dialogTitle
*/
FileDialog(std::function<int(tagOFNA*)> p_callback, const std::string& p_dialogTitle);
/**
* Defines the initial directory (Where the FileDialog will open)
* @param p_initalDirectory
*/
void SetInitialDirectory(const std::string& p_initialDirectory);
/**
* Show the file dialog
* @param p_flags
*/
virtual void Show(EExplorerFlags p_flags = EExplorerFlags::DONTADDTORECENT | EExplorerFlags::FILEMUSTEXIST | EExplorerFlags::HIDEREADONLY | EExplorerFlags::NOCHANGEDIR);
/**
* Returns true if the file action succeeded
*/
bool HasSucceeded() const;
/**
* Returns the selected file name (Make sur that HasSucceeded() returned true before calling this method)
*/
std::string GetSelectedFileName();
/**
* Returns the selected file path (Make sur that HasSucceeded() returned true before calling this method)
*/
std::string GetSelectedFilePath();
/**
* Returns some information about the last error (Make sur that HasSucceeded() returned false before calling this method)
*/
std::string GetErrorInfo();
/**
* Returns true if the selected file exists
*/
bool IsFileExisting() const;
private:
void HandleError();
protected:
std::function<int(tagOFNA*)> m_callback;
const std::string m_dialogTitle;
std::string m_initialDirectory;
std::string m_filter;
std::string m_error;
std::string m_filename;
std::string m_filepath;
bool m_succeeded;
};
上來首先是一個列舉定義,事實上表示了對話框的性質,比如第一個READONLY就表示只讀,這個看名字很明白了,不容易理解的還有注釋,因為定義的比較多,所以我就不一一說明意思了,總之知道它是對話框性質就好了,
接下來一系列的行內函式都是對運算子的多載,需要知道EExplorerFlags實際上就是一個數,所以做運算是很正常的,而多載運算子最后又把運算結果轉換成了EExplorerFlags型別,所以這些運算子多載后的作用就是改變對話框的性質,
接著它定義了FileDialog類這就是咱們檔案相關的對話框的基類了,具體的函式作用在這有注釋,我們到cpp檔案中實作的時候再細說,
FileDialog.cpp
因為檔案并不是很長,函式實作也并不算復雜,所以咱們把整個檔案放在一起講了,先上代碼:
OvWindowing::Dialogs::FileDialog::FileDialog(std::function<int(tagOFNA*)> p_callback, const std::string & p_dialogTitle) :
m_callback(p_callback),
m_dialogTitle(p_dialogTitle),
m_initialDirectory("")
{
}
void OvWindowing::Dialogs::FileDialog::SetInitialDirectory(const std::string & p_initialDirectory)
{
m_initialDirectory = p_initialDirectory;
}
void OvWindowing::Dialogs::FileDialog::Show(EExplorerFlags p_flags)
{
OPENFILENAME ofn;
if (!m_initialDirectory.empty())
m_filepath = m_initialDirectory;
m_filepath.resize(MAX_PATH);
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL; // If you have a window to center over, put its HANDLE here
ofn.lpstrFilter = m_filter.c_str();
ofn.lpstrFile = m_filepath.data();
ofn.nMaxFile = MAX_PATH;
ofn.lpstrTitle = m_dialogTitle.c_str();
if (!m_initialDirectory.empty())
ofn.lpstrInitialDir = m_initialDirectory.c_str();
ofn.Flags = static_cast<DWORD>(p_flags);
m_succeeded = m_callback(&ofn);
if (!m_succeeded)
HandleError();
else
m_filepath = m_filepath.c_str();
/* Extract filename from filepath */
m_filename.clear();
for (auto it = m_filepath.rbegin(); it != m_filepath.rend() && *it != '\\' && *it != '/'; ++it)
m_filename += *it;
std::reverse(m_filename.begin(), m_filename.end());
}
bool OvWindowing::Dialogs::FileDialog::HasSucceeded() const
{
return m_succeeded;
}
std::string OvWindowing::Dialogs::FileDialog::GetSelectedFileName()
{
return m_filename;
}
std::string OvWindowing::Dialogs::FileDialog::GetSelectedFilePath()
{
return m_filepath;
}
std::string OvWindowing::Dialogs::FileDialog::GetErrorInfo()
{
return m_error;
}
bool OvWindowing::Dialogs::FileDialog::IsFileExisting() const
{
return std::filesystem::exists(m_filepath);
}
void OvWindowing::Dialogs::FileDialog::HandleError()
{
switch (CommDlgExtendedError())
{
case CDERR_DIALOGFAILURE: m_error = "CDERR_DIALOGFAILURE"; break;
case CDERR_FINDRESFAILURE: m_error = "CDERR_FINDRESFAILURE"; break;
case CDERR_INITIALIZATION: m_error = "CDERR_INITIALIZATION"; break;
case CDERR_LOADRESFAILURE: m_error = "CDERR_LOADRESFAILURE"; break;
case CDERR_LOADSTRFAILURE: m_error = "CDERR_LOADSTRFAILURE"; break;
case CDERR_LOCKRESFAILURE: m_error = "CDERR_LOCKRESFAILURE"; break;
case CDERR_MEMALLOCFAILURE: m_error = "CDERR_MEMALLOCFAILURE"; break;
case CDERR_MEMLOCKFAILURE: m_error = "CDERR_MEMLOCKFAILURE"; break;
case CDERR_NOHINSTANCE: m_error = "CDERR_NOHINSTANCE"; break;
case CDERR_NOHOOK: m_error = "CDERR_NOHOOK"; break;
case CDERR_NOTEMPLATE: m_error = "CDERR_NOTEMPLATE"; break;
case CDERR_STRUCTSIZE: m_error = "CDERR_STRUCTSIZE"; break;
case FNERR_BUFFERTOOSMALL: m_error = "FNERR_BUFFERTOOSMALL"; break;
case FNERR_INVALIDFILENAME: m_error = "FNERR_INVALIDFILENAME"; break;
case FNERR_SUBCLASSFAILURE: m_error = "FNERR_SUBCLASSFAILURE"; break;
default: m_error = "You cancelled.";
}
}
首先是建構式,只做了屬性的賦值,p_callback型別中可以看到tagOFNA,這是OPENFILENAME相關的內部結構,比較復雜,咱們真的用到再講,m_dialogTitle是對話框的標題,
接下來的SetInitialDirectory()就是普通的賦值,更改m_initialDirectory,就是初始目錄的位置,
再下來是一個稍微復雜點的Show()函式,函式先定義了一個OPENFILENAME型別的變數,事實上是OPENFILENAMEA型別,而OPENFILENAMEA就是tagOFNA結構,接下來函式確認是否有初始路徑,如果有就把這個初始路徑賦值給當前檔案路徑,之后給當前檔案路徑分配更多的空間,也給ofn分配了一塊新的空間,接下來就是對ofn的屬性進行賦值,我們來講一下它的屬性,實際就是tagOFNA的屬性:
lStructSize是當前結構的大小,單位是位元組;
hwndOwner是所有者對話框視窗的句柄;
lpstrFilter是指定檔案名篩選字串,決定了對話框中“檔案型別”下拉式串列框中的內容,賦值比較復雜;
lpstrFile應指向一個包含檔案名的緩沖區;
nMaxFile指定lpstrFile引數指向的緩沖區的長度,單位為TCHARs;
lpstrTitle應當指向一個緩沖區,用來接收用戶選擇的檔案的檔案名和擴展名;
lpstrInitialDir是一個指向對話框的初始化目錄的以空字符結束的字串,在咱們的Show()函式里只有有初始路徑時才賦值;
Flags這個標志欄位決定了對話框的不同行為,
這就是我們在函式里提到的所有的屬性,想要全面了解tagOFNA的屬性的話,可以去看這篇文章,當然也可以看windows官方的說明,
在對ofn操作完成后,會用m_succeeded來接收一下回呼,確認是否創建賦值成功,如果失敗了,那么我們會回傳一個錯誤,如果成功的話,我們就把真實的檔案路徑賦給咱們當前實體的屬性,
再往下,就是如果咱們指定讀取了一個路徑,就會先清空已保存的當前路徑,然后讀取我們指定的路徑,
接下來的函式是HasSucceeded(),GetSelectedFileName(),GetSelectedFilePath(),GetErrorInfo(),IsFileExisting(),都是一行就結束的函式,我們在一起講,分別是用于讀取屬性m_succeeded,來確定是否成功生成;讀取選中檔案名,讀取選中檔案路徑,獲得報錯資訊,以及判斷我們選中的檔案是否存在,
最后是HandleError(),用于先從CommDlgExtendedError獲取錯誤型別,然后對不同的錯誤型別報不同的錯誤,
二、MessageBox
接下來講MessageBox這個檔案夾,先講頭檔案:
MessageBox.h
/**
* Displays a modal dialog box that contains a system icon,
* a set of buttons, and a brief application-specific message,
* such as status or error information
*/
class MessageBox
{
public:
/**
* Defines some severity levels for MessageBox instances
*/
enum class EMessageType
{
QUESTION = 0x00000020L,
INFORMATION = 0x00000040L,
WARNING = 0x00000030L,
ERROR = 0x00000010L
};
/**
* Defines some button layouts for MessageBox instances
*/
enum class EButtonLayout
{
OK = 0x00000000L,
OK_CANCEL = 0x00000001L,
YES_NO = 0x00000004L,
YES_NO_CANCEL = 0x00000003L,
RETRY_CANCEL = 0x00000005L,
ABORT_RETRY_IGNORE = 0x00000002L,
CANCEL_TRYAGAIN_CONTINUE = 0x00000006L,
HELP = 0x00004000L
};
/**
* Defines some actions that the MessageBox should provide
*/
enum class EUserAction
{
OK = 1,
CANCEL = 2,
YES = 6,
NO = 7,
CONTINUE = 11,
IGNORE = 5,
RETRY = 4,
TRYAGAIN = 10
};
/**
* Create the MessageBox
* @param p_title
* @param p_message
* @param p_messageType
* @param p_buttonLayout
* @param p_autoSpawn
*/
MessageBox(std::string p_title, std::string p_message, EMessageType p_messageType = EMessageType::INFORMATION, EButtonLayout p_buttonLayout = EButtonLayout::OK, bool p_autoSpawn = true);
/**
* Show the MessageBox on the screen
*/
void Spawn();
/**
* Return the user action
*/
const EUserAction& GetUserAction() const;
private:
std::string m_title;
std::string m_message;
EButtonLayout m_buttonLayout;
EMessageType m_messageType;
EUserAction m_userResult;
};
這個頭檔案定義了MessageBox,其實和咱們windows的MessageBox差不多,我們可以看到它有三個列舉:EMessageType是定義這個box的級別,是問題、資訊、警告還是錯誤;EButtonLayout則是咱們的按鈕型別;EUserAction則是用戶做出的選擇,其他的函式實作,我們在cpp檔案中講:
MessageBox.cpp
OvWindowing::Dialogs::MessageBox::MessageBox(std::string p_title, std::string p_message, EMessageType p_messageType, EButtonLayout p_buttonLayout, bool p_autoSpawn) :
m_title(p_title),
m_message(p_message),
m_buttonLayout(p_buttonLayout),
m_messageType(p_messageType)
{
if (p_autoSpawn)
Spawn();
}
const OvWindowing::Dialogs::MessageBox::EUserAction& OvWindowing::Dialogs::MessageBox::GetUserAction() const
{
return m_userResult;
}
void OvWindowing::Dialogs::MessageBox::Spawn()
{
int msgboxID = MessageBoxA
(
nullptr,
static_cast<LPCSTR>(m_message.c_str()),
static_cast<LPCSTR>(m_title.c_str()),
static_cast<UINT>(m_messageType) | static_cast<UINT>(m_buttonLayout) | MB_DEFBUTTON2
);
m_userResult = static_cast<EUserAction>(msgboxID);
}
首先是建構式,同樣也是比較簡單的賦值,唯一的一個判斷是是否自動顯現,如果是的話就顯現視窗,
GetUserAction()只是回傳一個屬性,這個屬性是用于記錄用戶的動作結果,
Spawn()是用于顯示視窗的,其實就是呼叫了windows的函式MessageBoxA(),這個函式會顯示一個對話框,之后會回傳的整數表示用戶所點擊的按鈕,所以我們會把那個得到的整數值存入m_userResult,也就是GetUserAction()回傳的屬性,
三、OpenFileDialog
接下來是OpenFileDialog檔案夾內的檔案,
OpenFileDialog.h
/**
* Dialog window that asks the user to select a file from the disk
*/
class OpenFileDialog : public FileDialog
{
public:
/**
* Constructor
* @param p_dialogTitle
*/
OpenFileDialog(const std::string& p_dialogTitle);
/**
* Add a supported file type to the dialog window
* @param p_label
* @param p_filter
*/
void AddFileType(const std::string& p_label, const std::string& p_filter);
};
這個頭檔案很短,定義了一個OpenFileDialog類,是讓用戶從磁盤選擇一個檔案的對話框,具體的函式實作在cpp檔案中說明:
OpenFileDialog.cpp
OvWindowing::Dialogs::OpenFileDialog::OpenFileDialog(const std::string & p_dialogTitle) : FileDialog(GetOpenFileNameA, p_dialogTitle)
{
}
void OvWindowing::Dialogs::OpenFileDialog::AddFileType(const std::string & p_label, const std::string & p_filter)
{
m_filter += p_label + '\0' + p_filter + '\0';
}
這個實作實際上也很簡單,建構式就是呼叫繼承的FileDialog類的建構式,而AddFileType()也就是往我們的檔案過濾器添加了一種型別,讓我們能支持新的檔案格式罷了,
四、SaveFileDialog
這里是SaveFileDialog檔案夾內的檔案:
SaveFileDialog.h
/**
* Dialog window that asks the user to save a file to the disk
*/
class SaveFileDialog : public FileDialog
{
public:
/**
* Constructor
* @param p_dialogTitle
*/
SaveFileDialog(const std::string& p_dialogTitle);
/**
* Show the file dialog
* @param p_flags
*/
virtual void Show(EExplorerFlags p_flags = EExplorerFlags::DONTADDTORECENT | EExplorerFlags::FILEMUSTEXIST | EExplorerFlags::HIDEREADONLY | EExplorerFlags::NOCHANGEDIR) override;
/**
* Define the extension of the saved file
* @param p_label
* @param p_extension
*/
void DefineExtension(const std::string& p_label, const std::string& p_extension);
private:
void AddExtensionToFilePathAndName();
private:
std::string m_extension;
};
這個頭檔案也比較簡單,定義了一個SaveFileDialog類,是保存檔案到磁盤的對話框,具體函式實作看下面:
SaveFileDialog.cpp
OvWindowing::Dialogs::SaveFileDialog::SaveFileDialog(const std::string & p_dialogTitle) : FileDialog(GetSaveFileNameA, p_dialogTitle)
{
}
void OvWindowing::Dialogs::SaveFileDialog::Show(EExplorerFlags p_flags)
{
FileDialog::Show(p_flags);
if (m_succeeded)
AddExtensionToFilePathAndName();
}
void OvWindowing::Dialogs::SaveFileDialog::DefineExtension(const std::string & p_label, const std::string & p_extension)
{
m_filter = p_label + '\0' + '*' + p_extension + '\0';
m_extension = p_extension;
}
void OvWindowing::Dialogs::SaveFileDialog::AddExtensionToFilePathAndName()
{
if (m_filename.size() >= m_extension.size())
{
std::string fileEnd(m_filename.data() + m_filename.size() - m_extension.size(), m_filename.data() + m_filename.size());
if (fileEnd != m_extension)
{
m_filepath += m_extension;
m_filename += m_extension;
}
}
else
{
m_filepath += m_extension;
m_filename += m_extension;
}
}
首先,建構式還是直接呼叫繼承的FileDialog類的建構式;
Show()函式實際上也是呼叫FileDialog類中的同名函式,但是添加了一句,用處是如果成功加載,就呼叫我們下面的一個函式AddExtensionToFilePathAndName(),作用在下面再講;
DefineExtension()是定義我們要保存檔案的型別,事實上就是后綴名;
AddExtensionToFilePathAndName()是添加我們的后綴名到檔案路徑和檔案名,如果咱們的檔案已經有名字了,那就看看拓展名對不對的上,不行就換掉當前的拓展名,如果沒有名字,就直接加上我們給定的拓展名就好了,
Inputs
接下來我們講Inputs檔案夾內的檔案,這里面的檔案很簡單,就是普通的輸入相關的各種檔案,我們先把各個頭檔案中的定義講完了,再去看cpp檔案,
一、EKey.h
/**
* Keyboard keys
*/
enum class EKey
{
KEY_UNKNOWN = -1,
KEY_SPACE = 32,
KEY_APOSTROPHE = 39,
KEY_COMMA = 44,
KEY_MINUS = 45,
KEY_PERIOD = 46,
KEY_SLASH = 47,
KEY_0 = 48,
KEY_1 = 49,
KEY_2 = 50,
KEY_3 = 51,
KEY_4 = 52,
KEY_5 = 53,
KEY_6 = 54,
KEY_7 = 55,
KEY_8 = 56,
KEY_9 = 57,
KEY_SEMICOLON = 59,
KEY_EQUAL = 61,
KEY_A = 65,
KEY_B = 66,
KEY_C = 67,
KEY_D = 68,
KEY_E = 69,
KEY_F = 70,
KEY_G = 71,
KEY_H = 72,
KEY_I = 73,
KEY_J = 74,
KEY_K = 75,
KEY_L = 76,
KEY_M = 77,
KEY_N = 78,
KEY_O = 79,
KEY_P = 80,
KEY_Q = 81,
KEY_R = 82,
KEY_S = 83,
KEY_T = 84,
KEY_U = 85,
KEY_V = 86,
KEY_W = 87,
KEY_X = 88,
KEY_Y = 89,
KEY_Z = 90,
KEY_LEFT_BRACKET = 91,
KEY_BACKSLASH = 92,
KEY_RIGHT_BRACKET = 93,
KEY_GRAVE_ACCENT = 96,
KEY_WORLD_1 = 61,
KEY_WORLD_2 = 62,
KEY_ESCAPE = 256,
KEY_ENTER = 257,
KEY_TAB = 258,
KEY_BACKSPACE = 259,
KEY_INSERT = 260,
KEY_DELETE = 261,
KEY_RIGHT = 262,
KEY_LEFT = 263,
KEY_DOWN = 264,
KEY_UP = 265,
KEY_PAGE_UP = 266,
KEY_PAGE_DOWN = 267,
KEY_HOME = 268,
KEY_END = 269,
KEY_CAPS_LOCK = 280,
KEY_SCROLL_LOCK = 281,
KEY_NUM_LOCK = 282,
KEY_PRINT_SCREEN = 283,
KEY_PAUSE = 284,
KEY_F1 = 290,
KEY_F2 = 291,
KEY_F3 = 292,
KEY_F4 = 293,
KEY_F5 = 294,
KEY_F6 = 295,
KEY_F7 = 296,
KEY_F8 = 297,
KEY_F9 = 298,
KEY_F10 = 299,
KEY_F11 = 300,
KEY_F12 = 301,
KEY_F13 = 302,
KEY_F14 = 303,
KEY_F15 = 304,
KEY_F16 = 305,
KEY_F17 = 306,
KEY_F18 = 307,
KEY_F19 = 308,
KEY_F20 = 309,
KEY_F21 = 310,
KEY_F22 = 311,
KEY_F23 = 312,
KEY_F24 = 313,
KEY_F25 = 314,
KEY_KP_0 = 320,
KEY_KP_1 = 321,
KEY_KP_2 = 322,
KEY_KP_3 = 323,
KEY_KP_4 = 324,
KEY_KP_5 = 325,
KEY_KP_6 = 326,
KEY_KP_7 = 327,
KEY_KP_8 = 328,
KEY_KP_9 = 329,
KEY_KP_DECIMAL = 330,
KEY_KP_DIVIDE = 331,
KEY_KP_MULTIPLY = 332,
KEY_KP_SUBTRACT = 333,
KEY_KP_ADD = 334,
KEY_KP_ENTER = 335,
KEY_KP_EQUAL = 336,
KEY_LEFT_SHIFT = 340,
KEY_LEFT_CONTROL = 341,
KEY_LEFT_ALT = 342,
KEY_LEFT_SUPER = 343,
KEY_RIGHT_SHIFT = 344,
KEY_RIGHT_CONTROL = 345,
KEY_RIGHT_ALT = 346,
KEY_RIGHT_SUPER = 347,
KEY_MENU = 348
};
這個列舉是把鍵盤上的按鍵都給了個序號用于對應,非常明了,
二、EKeyState.h
/**
* Defines some states that can be applied to keyboard keys
*/
enum class EKeyState
{
KEY_UP = 0,
KEY_DOWN = 1
};
這個列舉定義了按鍵的狀態,是否被按下,
三、EMouseButton.h
/**
* Mouse buttons
*/
enum class EMouseButton
{
MOUSE_BUTTON_1 = 0,
MOUSE_BUTTON_2 = 1,
MOUSE_BUTTON_3 = 2,
MOUSE_BUTTON_4 = 3,
MOUSE_BUTTON_5 = 4,
MOUSE_BUTTON_6 = 5,
MOUSE_BUTTON_7 = 6,
MOUSE_BUTTON_8 = 7,
MOUSE_BUTTON_LEFT = 0,
MOUSE_BUTTON_RIGHT = 1,
MOUSE_BUTTON_MIDDLE = 2
};
和EKey一樣,這個列舉定義了滑鼠的按鍵映射,
四、EMouseButtonState.h
/**
* Defines some states that can be applied to mouse buttons
*/
enum class EMouseButtonState
{
MOUSE_UP = 0,
MOUSE_DOWN = 1
};
和EKeyState一樣,用于標識滑鼠按鍵的狀態,是否被按下,
五、InputManager
接下來這個類有頭檔案和cpp檔案,
InputManager.h
/**
* Handles inputs (Mouse and keyboard)
*/
class InputManager
{
public:
/**
* Create the window
* @param p_windowSettings
*/
InputManager(Window& p_window);
/**
* Destroy the input manager by removing listeners on the window
*/
~InputManager();
/**
* Return the current state of the given key
* @param p_key
*/
EKeyState GetKeyState(EKey p_key) const;
/**
* Return the current state of the given mouse button
* @param p_button
*/
EMouseButtonState GetMouseButtonState(EMouseButton p_button) const;
/**
* Return true if the given key has been pressed during the frame
* @param p_key
*/
bool IsKeyPressed(EKey p_key) const;
/**
* Return true if the given key has been released during the frame
* @param p_key
*/
bool IsKeyReleased(EKey p_key) const;
/**
* Return true if the given mouse button has been pressed during the frame
* @param p_button
*/
bool IsMouseButtonPressed(EMouseButton p_button) const;
/**
* Return true if the given mouse button has been released during the frame
* @param p_button
*/
bool IsMouseButtonReleased(EMouseButton p_button) const;
/**
* Return the current mouse position relative to the window
*/
std::pair<double, double> GetMousePosition() const;
/**
* Clear any event occured
* @note Should be called at the end of every game tick
*/
void ClearEvents();
private:
void OnKeyPressed(int p_key);
void OnKeyReleased(int p_key);
void OnMouseButtonPressed(int p_button);
void OnMouseButtonReleased(int p_button);
private:
Window& m_window;
OvTools::Eventing::ListenerID m_keyPressedListener;
OvTools::Eventing::ListenerID m_keyReleasedListener;
OvTools::Eventing::ListenerID m_mouseButtonPressedListener;
OvTools::Eventing::ListenerID m_mouseButtonReleasedListener;
std::unordered_map<EKey, EKeyState> m_keyEvents;
std::unordered_map<EMouseButton, EMouseButtonState> m_mouseButtonEvents;
};
具體函式實作在分析cpp檔案的時候說,
InputManager.cpp
OvWindowing::Inputs::InputManager::InputManager(Window& p_window) : m_window(p_window)
{
m_keyPressedListener = m_window.KeyPressedEvent.AddListener(std::bind(&InputManager::OnKeyPressed, this, std::placeholders::_1));
m_keyReleasedListener = m_window.KeyReleasedEvent.AddListener(std::bind(&InputManager::OnKeyReleased, this, std::placeholders::_1));
m_mouseButtonPressedListener = m_window.MouseButtonPressedEvent.AddListener(std::bind(&InputManager::OnMouseButtonPressed, this, std::placeholders::_1));
m_mouseButtonReleasedListener = m_window.MouseButtonReleasedEvent.AddListener(std::bind(&InputManager::OnMouseButtonReleased, this, std::placeholders::_1));
}
OvWindowing::Inputs::InputManager::~InputManager()
{
m_window.KeyPressedEvent.RemoveListener(m_keyPressedListener);
m_window.KeyReleasedEvent.RemoveListener(m_keyReleasedListener);
m_window.MouseButtonPressedEvent.RemoveListener(m_mouseButtonPressedListener);
m_window.MouseButtonReleasedEvent.RemoveListener(m_mouseButtonReleasedListener);
}
OvWindowing::Inputs::EKeyState OvWindowing::Inputs::InputManager::GetKeyState(EKey p_key) const
{
switch (glfwGetKey(m_window.GetGlfwWindow(), static_cast<int>(p_key)))
{
case GLFW_PRESS: return EKeyState::KEY_DOWN;
case GLFW_RELEASE: return EKeyState::KEY_UP;
}
return EKeyState::KEY_UP;
}
OvWindowing::Inputs::EMouseButtonState OvWindowing::Inputs::InputManager::GetMouseButtonState(EMouseButton p_button) const
{
switch (glfwGetMouseButton(m_window.GetGlfwWindow(), static_cast<int>(p_button)))
{
case GLFW_PRESS: return EMouseButtonState::MOUSE_DOWN;
case GLFW_RELEASE: return EMouseButtonState::MOUSE_UP;
}
return EMouseButtonState::MOUSE_UP;
}
bool OvWindowing::Inputs::InputManager::IsKeyPressed(EKey p_key) const
{
return m_keyEvents.find(p_key) != m_keyEvents.end() && m_keyEvents.at(p_key) == EKeyState::KEY_DOWN;
}
bool OvWindowing::Inputs::InputManager::IsKeyReleased(EKey p_key) const
{
return m_keyEvents.find(p_key) != m_keyEvents.end() && m_keyEvents.at(p_key) == EKeyState::KEY_UP;
}
bool OvWindowing::Inputs::InputManager::IsMouseButtonPressed(EMouseButton p_button) const
{
return m_mouseButtonEvents.find(p_button) != m_mouseButtonEvents.end() && m_mouseButtonEvents.at(p_button) == EMouseButtonState::MOUSE_DOWN;
}
bool OvWindowing::Inputs::InputManager::IsMouseButtonReleased(EMouseButton p_button) const
{
return m_mouseButtonEvents.find(p_button) != m_mouseButtonEvents.end() && m_mouseButtonEvents.at(p_button) == EMouseButtonState::MOUSE_UP;
}
std::pair<double, double> OvWindowing::Inputs::InputManager::GetMousePosition() const
{
std::pair<double, double> result;
glfwGetCursorPos(m_window.GetGlfwWindow(), &result.first, &result.second);
return result;
}
void OvWindowing::Inputs::InputManager::ClearEvents()
{
m_keyEvents.clear();
m_mouseButtonEvents.clear();
}
void OvWindowing::Inputs::InputManager::OnKeyPressed(int p_key)
{
m_keyEvents[static_cast<EKey>(p_key)] = EKeyState::KEY_DOWN;
}
void OvWindowing::Inputs::InputManager::OnKeyReleased(int p_key)
{
m_keyEvents[static_cast<EKey>(p_key)] = EKeyState::KEY_UP;
}
void OvWindowing::Inputs::InputManager::OnMouseButtonPressed(int p_button)
{
m_mouseButtonEvents[static_cast<EMouseButton>(p_button)] = EMouseButtonState::MOUSE_DOWN;
}
void OvWindowing::Inputs::InputManager::OnMouseButtonReleased(int p_button)
{
m_mouseButtonEvents[static_cast<EMouseButton>(p_button)] = EMouseButtonState::MOUSE_UP;
}
我們一個個說:
首先建構式,除了把p_window賦給m_window外,還創造了多個監聽,用于得知鍵盤和滑鼠的按鍵是按下了還是釋放了,
解構式則是把咱們之前創建的監聽給移除了,
GetKeyState()用于獲得給定鍵盤按鍵的狀態,內部實作是呼叫了glfw的函式,看當前按鍵在glfw下的狀態,之后轉換到我們定義的狀態,如果這個glfw的狀態在我們的定義中沒有對應,就當作沒有按下按鍵,
GetMouseButtonState()內部實作和作用都和GetKeyState()幾乎一樣,只是一個對鍵盤用,一個對滑鼠用,
IsKeyPressed(),IsKeyReleased(),IsMouseButtonPressed(),IsMouseButtonReleased()這幾個的實作也差不多,功能一看就知,判斷按鍵是被按下還是被釋放,實作都是在我們的事件序列中尋找對應按鍵并和我們需要判斷的狀態作比較,如果既能找到這個按鍵的事件又能和我們希望的狀態匹配,就回傳真值,
GetMousePosition()很簡單,就是獲得當前滑鼠位置,實作依靠glfw,
ClearEvents()是清空全部事件,實作就是把我們保存的鍵盤按鍵事件序列和滑鼠按鍵序列清空,
OnKeyPressed(),OnKeyReleased(),OnMouseButtonPressed(),OnMouseButtonReleased()又是一組類似的函式,用于實時更新按鍵的狀態,當監聽給出回呼時呼叫此函式,把對應按鍵在事件序列中更改狀態,
一些剩下的檔案
我們到這已經把OvWindowing中的檔案基本講完了,但是還有一些我們以前并未提到的檔案,我在此做個收尾作業,
一、DeviceSettings.h
/**
* Contains device settings
*/
struct DeviceSettings
{
/**
* specifies whether to create a debug OpenGL context, which may have additional error and
* performance issue reporting functionality. If OpenGL ES is requested, this hint is ignored
*/
bool debugProfile = false;
/**
* Specifies whether the OpenGL context should be forward-compatible, i.e. one where all functionality
* deprecated in the requested version of OpenGL is removed. This must only be used if the requested OpenGL
* version is 3.0 or above. If OpenGL ES is requested, this hint is ignored.
*/
bool forwardCompatibility = false;
/**
* Specify the client API major version that the created context must be compatible with. The exact
* behavior of these hints depend on the requested client API
*/
uint8_t contextMajorVersion = 3;
/**
* Specify the client API minor version that the created context must be compatible with. The exact
* behavior of these hints depend on the requested client API
*/
uint8_t contextMinorVersion = 2;
/**
* Defines the amount of samples to use (Requiered for multi-sampling)
*/
uint8_t samples = 4;
};
這些是視窗的一些環境設定,比如是否開啟除錯,是否向前兼容,當前版本號,以及采樣數(用于MSAA),一般這些都是定死的,修改的意義不是很大,我們只要知道有這些,可以改就行了,
二、Window.h
/**
* A simple OS-based window.
* It needs a Device (GLFW) to work
*/
class Window
{
public:
/* Inputs relatives */
OvTools::Eventing::Event<int> KeyPressedEvent;
OvTools::Eventing::Event<int> KeyReleasedEvent;
OvTools::Eventing::Event<int> MouseButtonPressedEvent;
OvTools::Eventing::Event<int> MouseButtonReleasedEvent;
/* Window events */
OvTools::Eventing::Event<uint16_t, uint16_t> ResizeEvent;
OvTools::Eventing::Event<uint16_t, uint16_t> FramebufferResizeEvent;
OvTools::Eventing::Event<int16_t, int16_t> MoveEvent;
OvTools::Eventing::Event<int16_t, int16_t> CursorMoveEvent;
OvTools::Eventing::Event<> MinimizeEvent;
OvTools::Eventing::Event<> MaximizeEvent;
OvTools::Eventing::Event<> GainFocusEvent;
OvTools::Eventing::Event<> LostFocusEvent;
OvTools::Eventing::Event<> CloseEvent;
/**
* Create the window
* @param p_device
* @param p_windowSettings
*/
Window(const Context::Device& p_device, const Settings::WindowSettings& p_windowSettings);
/**
* Destructor of the window, responsible of the GLFW window memory free
*/
~Window();
/**
* Set Icon
* @param p_filePath
*/
void SetIcon(const std::string& p_filePath);
/**
* Set Icon from memory
* @param p_data
* @param p_width
* @param p_height
*/
void SetIconFromMemory(uint8_t* p_data, uint32_t p_width, uint32_t p_height);
/**
* Find an instance of window with a given GLFWwindow
* @param p_glfwWindow
*/
static Window* FindInstance(GLFWwindow* p_glfwWindow);
/**
* Resize the window
* @param p_width
* @param p_height
*/
void SetSize(uint16_t p_width, uint16_t p_height);
/**
* Defines a minimum size for the window
* @param p_minimumWidth
* @param p_minimumHeight
* @note -1 (WindowSettings::DontCare) value means no limitation
*/
void SetMinimumSize(int16_t p_minimumWidth, int16_t p_minimumHeight);
/**
* Defines a maximum size for the window
* @param p_maximumWidth
* @param p_maximumHeight
* @note -1 (WindowSettings::DontCare) value means no limitation
*/
void SetMaximumSize(int16_t p_maximumWidth, int16_t p_maximumHeight);
/**
* Define a position for the window
* @param p_x
* @param p_y
*/
void SetPosition(int16_t p_x, int16_t p_y);
/**
* Minimize the window
*/
void Minimize() const;
/**
* Maximize the window
*/
void Maximize() const;
/**
* Restore the window
*/
void Restore() const;
/**
* Hides the specified window if it was previously visible
*/
void Hide() const;
/**
* Show the specified window if it was previously hidden
*/
void Show() const;
/**
* Focus the window
*/
void Focus() const;
/**
* Set the should close flag of the window to true
* @param p_value
*/
void SetShouldClose(bool p_value) const;
/**
* Return true if the window should close
*/
bool ShouldClose() const;
/**
* Set the window in fullscreen or windowed mode
* @param p_value (True for fullscreen mode, false for windowed)
*/
void SetFullscreen(bool p_value);
/**
* Switch the window to fullscreen or windowed mode depending
* on the current state
*/
void ToggleFullscreen();
/**
* Return true if the window is fullscreen
*/
bool IsFullscreen() const;
/**
* Return true if the window is hidden
*/
bool IsHidden() const;
/**
* Return true if the window is visible
*/
bool IsVisible() const;
/**
* Return true if the windows is maximized
*/
bool IsMaximized() const;
/**
* Return true if the windows is minimized
*/
bool IsMinimized() const;
/**
* Return true if the windows is focused
*/
bool IsFocused() const;
/**
* Return true if the windows is resizable
*/
bool IsResizable() const;
/**
* Return true if the windows is decorated
*/
bool IsDecorated() const;
/**
* Define the window as the current context
*/
void MakeCurrentContext() const;
/**
* Handle the buffer swapping with the current window
*/
void SwapBuffers() const;
/**
* Define a mode for the mouse cursor
* @param p_cursorMode
*/
void SetCursorMode(Cursor::ECursorMode p_cursorMode);
/**
* Define a shape to apply to the current cursor
* @param p_cursorShape
*/
void SetCursorShape(Cursor::ECursorShape p_cursorShape);
/**
* Move the cursor to the given position
*/
void SetCursorPosition(int16_t p_x, int16_t p_y);
/**
* Define a title for the window
* @param p_title
*/
void SetTitle(const std::string& p_title);
/**
* Defines a refresh rate (Use WindowSettings::DontCare to use the highest available refresh rate)
* @param p_refreshRate
* @note You need to switch to fullscreen mode to apply this effect (Or leave fullscreen and re-apply)
*/
void SetRefreshRate(int32_t p_refreshRate);
/**
* Return the title of the window
*/
std::string GetTitle() const;
/**
* Return the current size of the window
*/
std::pair<uint16_t, uint16_t> GetSize() const;
/**
* Return the current minimum size of the window
* @note -1 (WindowSettings::DontCare) values means no limitation
*/
std::pair<int16_t, int16_t> GetMinimumSize() const;
/**
* Return the current maximum size of the window
* @note -1 (WindowSettings::DontCare) values means no limitation
*/
std::pair<int16_t, int16_t> GetMaximumSize() const;
/**
* Return the current position of the window
*/
std::pair<int16_t, int16_t> GetPosition() const;
/**
* Return the framebuffer size (Viewport size)
*/
std::pair<uint16_t, uint16_t> GetFramebufferSize() const;
/**
* Return the current cursor mode
*/
Cursor::ECursorMode GetCursorMode() const;
/**
* Return the current cursor shape
*/
Cursor::ECursorShape GetCursorShape() const;
/**
* Return the current refresh rate (Only applied to the fullscreen mode).
* If the value is -1 (WindowSettings::DontCare) the highest refresh rate will be used
*/
int32_t GetRefreshRate() const;
/**
* Return GLFW window
*/
GLFWwindow* GetGlfwWindow() const;
private:
void CreateGlfwWindow(const Settings::WindowSettings& p_windowSettings);
/* Callbacks binding */
void BindKeyCallback() const;
void BindMouseCallback() const;
void BindResizeCallback() const;
void BindFramebufferResizeCallback() const;
void BindCursorMoveCallback() const;
void BindMoveCallback() const;
void BindIconifyCallback() const;
void BindFocusCallback() const;
void BindCloseCallback() const;
/* Event listeners */
void OnResize(uint16_t p_width, uint16_t p_height);
void OnMove(int16_t p_x, int16_t p_y);
/* Internal helpers */
void UpdateSizeLimit() const;
private:
/* This map is used by callbacks to find a "Window" instance out of a "GLFWwindow" instnace*/
static std::unordered_map<GLFWwindow*, Window*> __WINDOWS_MAP;
const Context::Device& m_device;
GLFWwindow* m_glfwWindow;
/* Window settings */
std::string m_title;
std::pair<uint16_t, uint16_t> m_size;
std::pair<int16_t, int16_t> m_minimumSize;
std::pair<int16_t, int16_t> m_maximumSize;
std::pair<int16_t, int16_t> m_position;
bool m_fullscreen;
int32_t m_refreshRate;
Cursor::ECursorMode m_cursorMode;
Cursor::ECursorShape m_cursorShape;
};
這個雖說我之前沒有直接提過,但是這里面的函式其實已經全部講過了,就在講Window.cpp的時候,所以我在這就不說了,需要的可以看代碼分析一,
總結
這樣的話,我們已經把Overload其中的一個模塊——OvWindowing給講完了,可喜可賀,之后咱會開始講OvEditor,也就是Overload的主體部分,加油!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/323248.html
標籤:其他
上一篇:C語言練習2---猜數字小游戲
