From 3b6b77d41e42899907e99c99d37acfb0459c4ea3 Mon Sep 17 00:00:00 2001 From: Louis <78240770+LouisStefanski-dev@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:11:57 -0700 Subject: [PATCH 1/2] Added invert boolean for listBox If invert is set to true, the listBox will show items from bottom to top. If it is set to false items are shown as normal(top to bottom). --- extensions/olcPGEX_QuickGUI.h | 2373 +++++++++++++++++---------------- 1 file changed, 1203 insertions(+), 1170 deletions(-) diff --git a/extensions/olcPGEX_QuickGUI.h b/extensions/olcPGEX_QuickGUI.h index 6b0c4ef8..e4810d25 100644 --- a/extensions/olcPGEX_QuickGUI.h +++ b/extensions/olcPGEX_QuickGUI.h @@ -1,1170 +1,1203 @@ -/* - OneLoneCoder - QuickGUI v1.02 - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - A semi-immediate mode GUI for very simple GUI stuff. - Includes: - Label - Displays a single-line string - TextBox - Click to enter/edit single-line text - Button - A clickable labelled rectangle - CheckBox - A clickable labelled rectangle that retains state - ImageButton - A Button with an image instead of text - ImageCheckBox- A CheckBox with an image instead of text - Slider - An omnidirectional draggable handle between two values - ListBox - A list of strings, that can be scrolled and an item selected - - License (OLC-3) - ~~~~~~~~~~~~~~~ - - Copyright 2018 - 2021 OneLoneCoder.com - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions or derivations of source code must retain the above - copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions or derivative works in binary form must reproduce - the above copyright notice. This list of conditions and the following - disclaimer must be reproduced in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Links - ~~~~~ - YouTube: https://www.youtube.com/javidx9 - Discord: https://discord.gg/WhwHUMV - Twitter: https://www.twitter.com/javidx9 - Twitch: https://www.twitch.tv/javidx9 - GitHub: https://www.github.com/onelonecoder - Homepage: https://www.onelonecoder.com - - Author - ~~~~~~ - David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 - - Changes - ~~~~~~~ - v1.01 +Moved Slider::fGrabRad into "theme" - +Manager::CopyThemeFrom() - copies theme attributes from a different manager - +ListBox - Displays a vector of strings - v1.02 +ImageButton - +ImageCheckBox - +ListBox::bSelectionChanged flag, true when list selected item changes - =Fix - Text box mouse behaviours, mouse release is now meaningless - +CheckBox Fix for decal display - -*/ - -#ifndef OLC_PGEX_QUICKGUI_H -#define OLC_PGEX_QUICKGUI_H - -#include "olcPixelGameEngine.h" - - -namespace olc::QuickGUI -{ - class Manager; - - // Virtual base class for all controls - class BaseControl - { - public: - BaseControl(olc::QuickGUI::Manager& manager); - virtual ~BaseControl(); - - public: - // Switches the control on/off - void Enable(const bool bEnable); - // Sets whether or not the control is interactive/displayed - bool bVisible = true; - - // True on single frame control begins being manipulated - bool bPressed = false; - // True on all frames control is under user manipulation - bool bHeld = false; - // True on single frame control ceases being manipulated - bool bReleased = false; - - public: - // Updates the controls behvaiour - virtual void Update(olc::PixelGameEngine* pge) = 0; - // Draws the control using "sprite" based CPU operations - virtual void Draw(olc::PixelGameEngine* pge) = 0; - // Draws the control using "decal" based GPU operations - virtual void DrawDecal(olc::PixelGameEngine* pge) = 0; - - protected: - // Controls are related to a manager, where the theme resides - // and control groups can be implemented - olc::QuickGUI::Manager& m_manager; - - // All controls exists in one of four states - // Disabled - Greyed out and not interactive - // Normal - interactive and operational - // Hover - currently under the users mouse focus - // Click - user is interacting with the control - enum class State { Disabled, Normal, Hover, Click } m_state = State::Normal; - - // To add a "swish" to things, controls can fade between states - float m_fTransition = 0.0; - }; - - - // A QuickGUI::Manager acts as a convenient grouping of controls - class Manager - { - public: - // Construct Manager, bCleanUpForMe will automatically DELETE any controls - // given to this manager via AddControl() if true - Manager(const bool bCleanUpForMe = true); - virtual ~Manager(); - - public: - // Add a gui element derived form BaseControl to this manager - void AddControl(BaseControl* control); - // Updates all controls this manager operates - void Update(olc::PixelGameEngine* pge); - // Draws as "sprite" all controls this manager operates - void Draw(olc::PixelGameEngine* pge); - // Draws as "decal" all controls this manager operates - void DrawDecal(olc::PixelGameEngine* pge); - - public: // This managers "Theme" can be set here - // Various element colours - olc::Pixel colNormal = olc::DARK_BLUE; - olc::Pixel colHover = olc::BLUE; - olc::Pixel colClick = olc::CYAN; - olc::Pixel colDisable = olc::DARK_GREY; - olc::Pixel colBorder = olc::WHITE; - olc::Pixel colText = olc::WHITE; - // Speed to transiton from Normal -> Hover - float fHoverSpeedOn = 10.0f; - // Speed to transiton from Hover -> Normal - float fHoverSpeedOff = 4.0f; - // Size of grab handle - float fGrabRad = 8.0f; - // Copy all theme attributes into a different manager object - void CopyThemeFrom(const Manager& manager); - - private: - // Should this manager call delete on the controls it opeerates? - bool m_bEraseControlsOnDestroy = true; - // Container of controls - std::vector m_vControls; - }; - - - // Creates a Label Control - it's just text! - class Label : public BaseControl - { - public: - Label(olc::QuickGUI::Manager& manager, // Associate with a Manager - const std::string& text, // Text to display - const olc::vf2d& pos, // Location of label top-left - const olc::vf2d& size); // Size of label - - public: - // Position of button - olc::vf2d vPos; - // Size of button - olc::vf2d vSize; - // Text displayed on button - std::string sText; - // Show a border? - bool bHasBorder = false; - // Show a background? - bool bHasBackground = false; - // Where should the text be positioned? - enum class Alignment - {Left, Centre, Right} nAlign = Alignment::Centre; - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - class TextBox : public Label - { - public: - TextBox(olc::QuickGUI::Manager& manager, // Associate with a Manager - const std::string& text, // Text to display - const olc::vf2d& pos, // Location of text box top-left - const olc::vf2d& size); // Size of text box - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - - protected: - bool m_bTextEdit = false; - - }; - - // Creates a Button Control - a clickable, labelled rectangle - class Button : public BaseControl - { - public: - Button(olc::QuickGUI::Manager& manager, // Associate with a Manager - const std::string& text, // Text to display - const olc::vf2d& pos, // Location of button top-left - const olc::vf2d& size); // Size of button - - public: - // Position of button - olc::vf2d vPos; - // Size of button - olc::vf2d vSize; - // Text displayed on button - std::string sText; - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - // Creates a Button Control - a clickable, labelled rectangle - class CheckBox : public Button - { - public: - CheckBox(olc::QuickGUI::Manager& manager, // Associate with a Manager - const std::string& text, // Text to display - const bool check, // Is checked or not? - const olc::vf2d& pos, // Location of button top-left - const olc::vf2d& size); // Size of button - - public: - bool bChecked = false; - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - class ImageButton : public Button - { - public: - ImageButton(olc::QuickGUI::Manager& manager, // Associate with a Manager - const olc::Renderable &icon, // Text to display - const olc::vf2d& pos, // Location of button top-left - const olc::vf2d& size); // Size of button - - public: - const olc::Renderable& pIcon; - - public: - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - class ImageCheckBox : public ImageButton - { - public: - ImageCheckBox(olc::QuickGUI::Manager& manager, // Associate with a Manager - const olc::Renderable& icon, // Text to display - const bool check, // Is checked or not? - const olc::vf2d& pos, // Location of button top-left - const olc::vf2d& size); // Size of button - - public: - bool bChecked = false; - - public: - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - - // Creates a Slider Control - a grabbable handle that slides between two locations - class Slider : public BaseControl - { - public: - Slider(olc::QuickGUI::Manager& manager, // Associate with a Manager - const olc::vf2d& posmin, // Screen location of "minimum" - const olc::vf2d& posmax, // Screen location of "maximum" - const float valmin, // Value of minimum - const float valmax, // Value of maximum - const float value); // Starting value - - public: - // Minium value - float fMin = -100.0f; - // Maximum value - float fMax = +100.0f; - // Current value - float fValue = 0.0f; - - // Location of minimum/start - olc::vf2d vPosMin; - // Location of maximum/end - olc::vf2d vPosMax; - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - - class ListBox : public BaseControl - { - public: - ListBox(olc::QuickGUI::Manager& manager, // Associate with a Manager - std::vector& vList, - const olc::vf2d& pos, // Location of list top-left - const olc::vf2d& size); // Size of list - - // Position of list - olc::vf2d vPos; - // Size of list - olc::vf2d vSize; - // Show a border? - bool bHasBorder = true; - // Show a background? - bool bHasBackground = true; - - public: - Slider *m_pSlider = nullptr; - Manager m_group; - size_t m_nVisibleItems = 0; - std::vector& m_vList; - - public: - // Item currently selected - size_t nSelectedItem = 0; - size_t nPreviouslySelectedItem = 0; - // Has selection changed? - bool bSelectionChanged = false; - - public: // BaseControl overrides - void Update(olc::PixelGameEngine* pge) override; - void Draw(olc::PixelGameEngine* pge) override; - void DrawDecal(olc::PixelGameEngine* pge) override; - }; - - - class ModalDialog : public olc::PGEX - { - public: - ModalDialog(); - - public: - void ShowFileOpen(const std::string& sPath); - - protected: - virtual bool OnBeforeUserUpdate(float& fElapsedTime) override; - - private: - bool m_bShowDialog = false; - - Manager m_manFileSelect; - ListBox* m_listVolumes = nullptr; - ListBox* m_listDirectory = nullptr; - ListBox* m_listFiles = nullptr; - - std::vector m_vVolumes; - std::vector m_vDirectory; - std::vector m_vFiles; - std::filesystem::path m_path; - }; -} - - -#ifdef OLC_PGEX_QUICKGUI -#undef OLC_PGEX_QUICKGUI -namespace olc::QuickGUI -{ - -#pragma region BaseControl - BaseControl::BaseControl(olc::QuickGUI::Manager& manager) : m_manager(manager) - { - m_manager.AddControl(this); - } - - BaseControl::~BaseControl() - { - - } - - void BaseControl::Enable(const bool bEnable) - { - m_state = bEnable ? State::Normal : State::Disabled; - } -#pragma endregion - -#pragma region Manager - Manager::Manager(const bool bCleanUpForMe) - { - m_bEraseControlsOnDestroy = bCleanUpForMe; - } - - Manager::~Manager() - { - if (m_bEraseControlsOnDestroy) - for (auto& p : m_vControls) - delete p; - - m_vControls.clear(); - } - - void Manager::AddControl(BaseControl* control) - { - m_vControls.push_back(control); - } - - void Manager::Update(olc::PixelGameEngine* pge) - { - for (auto& p : m_vControls) p->Update(pge); - } - - void Manager::Draw(olc::PixelGameEngine* pge) - { - for (auto& p : m_vControls) p->Draw(pge); - } - - void Manager::DrawDecal(olc::PixelGameEngine* pge) - { - for (auto& p : m_vControls) p->DrawDecal(pge); - } - - void Manager::CopyThemeFrom(const Manager& manager) - { - this->colBorder = manager.colBorder; - this->colClick = manager.colClick; - this->colDisable = manager.colDisable; - this->colHover = manager.colHover; - this->colNormal = manager.colNormal; - this->colText = manager.colText; - this->fGrabRad = manager.fGrabRad; - this->fHoverSpeedOff = manager.fHoverSpeedOff; - this->fHoverSpeedOn = manager.fHoverSpeedOn; - } -#pragma endregion - -#pragma region Label - Label::Label(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) - : BaseControl(manager) - { - vPos = pos; vSize = size; sText = text; - } - - void Label::Update(olc::PixelGameEngine* pge) - { - - } - - void Label::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - { - pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - } - - if(bHasBorder) - pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); - - olc::vf2d vText = pge->GetTextSizeProp(sText); - switch (nAlign) - { - case Alignment::Left: - pge->DrawStringProp(olc::vf2d( vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f ), sText, m_manager.colText); - break; - case Alignment::Centre: - pge->DrawStringProp(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - break; - case Alignment::Right: - pge->DrawStringProp(olc::vf2d{ vPos.x + vSize.x - vText.x - 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); - break; - } - } - - void Label::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - { - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - } - - if (bHasBorder) - { - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2,2), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - } - - olc::vf2d vText = pge->GetTextSizeProp(sText); - switch (nAlign) - { - case Alignment::Left: - pge->DrawStringPropDecal({ vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); - break; - case Alignment::Centre: - pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - break; - case Alignment::Right: - pge->DrawStringPropDecal({ vPos.x + vSize.x - vText.x - 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); - break; - } - } -#pragma endregion - - -#pragma region TextBox - TextBox::TextBox(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) - : Label(manager, text, pos, size) - { - nAlign = Alignment::Left; - bHasBorder = true; - bHasBackground = false; - } - - void TextBox::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - bPressed = false; - bReleased = false; - - olc::vf2d vMouse = pge->GetMousePos(); - - if (vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && - vMouse.y >= vPos.y && vMouse.y < vPos.y + vSize.y) - { - // Released inside box does nothing to me, but i may have - // to finish off the neighbours... oo err - bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; - bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; - - if (bPressed && pge->IsTextEntryEnabled() && !m_bTextEdit) - { - pge->TextEntryEnable(false); - } - - - if (bPressed && !pge->IsTextEntryEnabled() && !m_bTextEdit) - { - pge->TextEntryEnable(true, sText); - m_bTextEdit = true; - } - - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; - - - } - else - { - // Released outside box - bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; - bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; - - if (bPressed && m_bTextEdit) - { - sText = pge->TextEntryGetString(); - pge->TextEntryEnable(false); - m_bTextEdit = false; - } - } - - if (m_bTextEdit && pge->IsTextEntryEnabled()) - sText = pge->TextEntryGetString(); - } - - void TextBox::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - { - pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - } - - if (bHasBorder) - pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); - - if (m_bTextEdit && pge->IsTextEntryEnabled()) - { - // Draw Cursor - int32_t i = pge->TextEntryGetCursor(); - olc::vf2d vCursorPos = pge->GetTextSizeProp(sText.substr(0, i)); - pge->FillRect(olc::vf2d(vPos.x + 2.0f + vCursorPos.x, (vPos.y + (vSize.y - 10.0f) * 0.5f)), { 2, 10 }, m_manager.colText); - } - - // Draw Text - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringProp(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), sText, m_manager.colText); - - } - - void TextBox::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - { - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - } - - if (bHasBorder) - { - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - } - - if (m_bTextEdit && pge->IsTextEntryEnabled()) - { - // Draw Cursor - int32_t i = pge->TextEntryGetCursor(); - olc::vf2d vCursorPos = pge->GetTextSizeProp(sText.substr(0, i)); - pge->FillRectDecal(olc::vf2d(vPos.x + 2.0f + vCursorPos.x, (vPos.y + (vSize.y - 10.0f) * 0.5f)), { 2, 10 }, m_manager.colText); - } - - // Draw Text - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringPropDecal(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), sText, m_manager.colText); - } -#pragma endregion - -#pragma region Button - Button::Button(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) - : BaseControl(manager) - { - vPos = pos; vSize = size; sText = text; - } - - void Button::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - bPressed = false; - bReleased = false; - float fElapsedTime = pge->GetElapsedTime(); - - olc::vf2d vMouse = pge->GetMousePos(); - if (m_state != State::Click) - { - if (vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && - vMouse.y >= vPos.y && vMouse.y < vPos.y + vSize.y) - { - m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; - m_state = State::Hover; - - bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; - if (bPressed) - { - m_state = State::Click; - } - - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; - } - else - { - m_fTransition -= fElapsedTime * m_manager.fHoverSpeedOff; - m_state = State::Normal; - } - } - else - { - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; - bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; - if (bReleased) m_state = State::Normal; - } - - m_fTransition = std::clamp(m_fTransition, 0.0f, 1.0f); - } - - void Button::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - switch (m_state) - { - case State::Disabled: - pge->FillRect(vPos, vSize, m_manager.colDisable); - break; - case State::Normal: - case State::Hover: - pge->FillRect(vPos, vSize, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); - break; - case State::Click: - pge->FillRect(vPos, vSize, m_manager.colClick); - break; - } - - pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringProp(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - } - - void Button::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - switch (m_state) - { - case State::Disabled: - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colDisable); - break; - case State::Normal: - case State::Hover: - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); - break; - case State::Click: - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colClick); - break; - } - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - } -#pragma endregion - - -#pragma region ImageButton - ImageButton::ImageButton(olc::QuickGUI::Manager& manager, const olc::Renderable& icon, const olc::vf2d& pos, const olc::vf2d& size) - : Button(manager, "", pos, size), pIcon(icon) - { - - } - - void ImageButton::Draw(olc::PixelGameEngine* pge) - { - Button::Draw(pge); - pge->DrawSprite(vPos + olc::vi2d(4, 4), pIcon.Sprite()); - } - - void ImageButton::DrawDecal(olc::PixelGameEngine* pge) - { - Button::DrawDecal(pge); - pge->DrawDecal(vPos + olc::vi2d(4, 4), pIcon.Decal()); - } -#pragma endregion - - -#pragma region ImageCheckBox - ImageCheckBox::ImageCheckBox(olc::QuickGUI::Manager& manager, const olc::Renderable& gfx, const bool check, const olc::vf2d& pos, const olc::vf2d& size) - : ImageButton(manager, gfx, pos, size) - { - bChecked = check; - } - - void ImageCheckBox::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - ImageButton::Update(pge); - if (bPressed) bChecked = !bChecked; - } - - void ImageCheckBox::Draw(olc::PixelGameEngine* pge) - { - ImageButton::Draw(pge); - - if (bChecked) - pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(5, 5), m_manager.colBorder); - } - - void ImageCheckBox::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - ImageButton::DrawDecal(pge); - - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(2, 2), vSize - olc::vf2d(4, 4), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - } -#pragma endregion - - -#pragma region CheckBox - CheckBox::CheckBox(olc::QuickGUI::Manager& manager, const std::string& text, const bool check, const olc::vf2d& pos, const olc::vf2d& size) - : Button(manager, text, pos, size) - { - bChecked = check; - } - - void CheckBox::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - Button::Update(pge); - if (bPressed) - bChecked = !bChecked; - } - - void CheckBox::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - Button::Draw(pge); - - if (bChecked) - pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(5, 5), m_manager.colBorder); - } - - void CheckBox::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - Button::DrawDecal(pge); - - if (bChecked) - { - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(2, 2), vSize - olc::vf2d(4, 4), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - } - - olc::vf2d vText = pge->GetTextSizeProp(sText); - pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); - } -#pragma endregion - -#pragma region Slider - Slider::Slider(olc::QuickGUI::Manager& manager, const olc::vf2d& posmin, const olc::vf2d& posmax, const float valmin, const float valmax, const float value) - : BaseControl(manager) - { - vPosMin = posmin; vPosMax = posmax; fMin = valmin; fMax = valmax; fValue = value; - } - - void Slider::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - float fElapsedTime = pge->GetElapsedTime(); - - olc::vf2d vMouse = pge->GetMousePos(); - bHeld = false; - if (m_state == State::Click) - { - olc::vf2d d = vPosMax - vPosMin; - float u = d.dot(vMouse - vPosMin) / d.mag2(); - fValue = u * (fMax - fMin) + fMin; - bHeld = true; - } - else - { - olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); - if ((vMouse - vSliderPos).mag2() <= int32_t(m_manager.fGrabRad) * int32_t(m_manager.fGrabRad)) - { - m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; - m_state = State::Hover; - if (pge->GetMouse(olc::Mouse::LEFT).bPressed) - { - m_state = State::Click; - bPressed = true; - } - } - else - m_state = State::Normal; - } - - if (pge->GetMouse(olc::Mouse::LEFT).bReleased) - { - m_state = State::Normal; - bReleased = true; - } - - if (m_state == State::Normal) - { - m_fTransition -= fElapsedTime * m_manager.fHoverSpeedOff; - m_state = State::Normal; - bHeld = false; - } - - fValue = std::clamp(fValue, fMin, fMax); - m_fTransition = std::clamp(m_fTransition, 0.0f, 1.0f); - } - - void Slider::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - pge->DrawLine(vPosMin, vPosMax, m_manager.colBorder); - olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); - - switch (m_state) - { - case State::Disabled: - pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colDisable); - break; - case State::Normal: - case State::Hover: - pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); - break; - case State::Click: - pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colClick); - break; - } - - - pge->DrawCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colBorder); - } - - void Slider::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - pge->DrawLineDecal(vPosMin, vPosMax, m_manager.colBorder); - olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); - - switch (m_state) - { - case State::Disabled: - pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colDisable); - break; - case State::Normal: - case State::Hover: - pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); - break; - case State::Click: - pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colClick); - break; - } - - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - } - - -#pragma endregion - -#pragma region ListBox - ListBox::ListBox(olc::QuickGUI::Manager& manager, std::vector& vList, const olc::vf2d& pos, const olc::vf2d& size) - : BaseControl(manager), m_vList(vList) - { - m_group.CopyThemeFrom(m_manager); - vPos = pos; - vSize = size; - m_pSlider = new Slider(m_group, { pos.x + size.x - m_manager.fGrabRad - 1, pos.y + m_manager.fGrabRad + 1 }, - { pos.x + size.x - m_manager.fGrabRad - 1, pos.y + size.y - m_manager.fGrabRad - 1 }, 0, float(m_vList.size()), 0); - } - - void ListBox::Update(olc::PixelGameEngine* pge) - { - if (m_state == State::Disabled || !bVisible) - return; - - - nPreviouslySelectedItem = nSelectedItem; - olc::vf2d vMouse = pge->GetMousePos() - vPos + olc::vi2d(2,0); - if (pge->GetMouse(olc::Mouse::LEFT).bPressed) - { - if (vMouse.x >= 0 && vMouse.x < vSize.x - (m_group.fGrabRad * 2) && vMouse.y >= 0 && vMouse.y < vSize.y) - { - - nSelectedItem = size_t(m_pSlider->fValue + vMouse.y / 10); - } - } - - nSelectedItem = std::clamp(nSelectedItem, size_t(0), m_vList.size()-1); - - bSelectionChanged = nSelectedItem != nPreviouslySelectedItem; - - - m_pSlider->fMax = float(m_vList.size()); - m_group.Update(pge); - } - - void ListBox::Draw(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - { - pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - } - - if (bHasBorder) - pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); - - - size_t idx0 = size_t(m_pSlider->fValue); - size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); - - olc::vf2d vTextPos = vPos + olc::vf2d(2,2); - for (size_t idx = idx0; idx < idx1; idx++) - { - if (idx == nSelectedItem) - pge->FillRect(vTextPos - olc::vi2d(1,1), {int32_t(vSize.x - m_group.fGrabRad * 2), 10}, m_group.colHover); - pge->DrawStringProp(vTextPos, m_vList[idx]); - vTextPos.y += 10; - } - - m_group.Draw(pge); - } - - void ListBox::DrawDecal(olc::PixelGameEngine* pge) - { - if (!bVisible) - return; - - if (bHasBackground) - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); - - size_t idx0 = size_t(m_pSlider->fValue); - size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); - - olc::vf2d vTextPos = vPos + olc::vf2d(2, 2); - for (size_t idx = idx0; idx < idx1; idx++) - { - if (idx == nSelectedItem) - pge->FillRectDecal(vTextPos - olc::vi2d(1, 1), { vSize.x - m_group.fGrabRad * 2.0f, 10.0f }, m_group.colHover); - pge->DrawStringPropDecal(vTextPos, m_vList[idx]); - vTextPos.y += 10; - } - - if (bHasBorder) - { - pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); - pge->SetDecalMode(olc::DecalMode::NORMAL); - } - - m_group.DrawDecal(pge); - } -#pragma endregion - - - -#pragma region Modal - ModalDialog::ModalDialog() : olc::PGEX(true) - { - - // Create File Open Dialog - olc::vi2d vScreenSize = pge->GetScreenSize(); - - m_listDirectory = new ListBox(m_manFileSelect, m_vDirectory, olc::vf2d(20, 20), olc::vf2d(300, 500)); - m_listFiles = new ListBox(m_manFileSelect, m_vFiles, olc::vf2d(330, 20), olc::vf2d(300, 500)); - - m_path = "/"; - for (auto const& dir_entry : std::filesystem::directory_iterator{ m_path }) - { - if(dir_entry.is_directory()) - m_vDirectory.push_back(dir_entry.path().filename().string()); - else - m_vFiles.push_back(dir_entry.path().filename().string()); - } - } - - void ModalDialog::ShowFileOpen(const std::string& sPath) - { - m_bShowDialog = true; - } - - bool ModalDialog::OnBeforeUserUpdate(float& fElapsedTime) - { - if(!m_bShowDialog) return false; - - m_manFileSelect.Update(this->pge); - - if (pge->GetKey(olc::Key::BACK).bPressed) - { - m_path = m_path.parent_path().string() + "/"; - //m_listDirectory->bSelectionChanged = true; - //m_listDirectory->nSelectedItem = 0; - } - - if (m_listDirectory->bSelectionChanged) - { - std::string sDirectory = m_vDirectory[m_listDirectory->nSelectedItem]; - /*if (sDirectory == "..") - m_path = m_path.parent_path().string() + "/"; - else - m_path += sDirectory+ "/";*/ - - - m_path += sDirectory + "/"; - // Reconstruct Lists - m_vDirectory.clear(); - - m_vFiles.clear(); - - - for (auto const& dir_entry : std::filesystem::directory_iterator{ m_path }) - { - if (dir_entry.is_directory() || dir_entry.is_other()) - m_vDirectory.push_back(dir_entry.path().filename().string()); - else - m_vFiles.push_back(dir_entry.path().filename().string()); - } - - //m_vDirectory.push_back(".."); - - //m_listFiles->nSelectedItem = 0; - //m_listDirectory->nSelectedItem = 0; - - } - - pge->DrawStringDecal({ 0,0 }, m_path.string()); - - - - - m_manFileSelect.DrawDecal(this->pge); - - - - if (pge->GetKey(olc::Key::ESCAPE).bPressed) - { - m_bShowDialog = false; - return false; - } - - return true; - } -#pragma endregion - -} -#endif // OLC_PGEX_QUICKGUI -#endif // OLC_PGEX_QUICKGUI_H \ No newline at end of file + +/* + OneLoneCoder - QuickGUI v1.02 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A semi-immediate mode GUI for very simple GUI stuff. + Includes: + Label - Displays a single-line string + TextBox - Click to enter/edit single-line text + Button - A clickable labelled rectangle + CheckBox - A clickable labelled rectangle that retains state + ImageButton - A Button with an image instead of text + ImageCheckBox- A CheckBox with an image instead of text + Slider - An omnidirectional draggable handle between two values + ListBox - A list of strings, that can be scrolled and an item selected + License (OLC-3) + ~~~~~~~~~~~~~~~ + Copyright 2018 - 2021 OneLoneCoder.com + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + Author + ~~~~~~ + David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 + Changes + ~~~~~~~ + v1.01 +Moved Slider::fGrabRad into "theme" + +Manager::CopyThemeFrom() - copies theme attributes from a different manager + +ListBox - Displays a vector of strings + v1.02 +ImageButton + +ImageCheckBox + +ListBox::bSelectionChanged flag, true when list selected item changes + =Fix - Text box mouse behaviours, mouse release is now meaningless + +CheckBox Fix for decal display +*/ + +#ifndef OLC_PGEX_QUICKGUI_H +#define OLC_PGEX_QUICKGUI_H + +#include "olcPixelGameEngine.h" + + +namespace olc::QuickGUI +{ + class Manager; + + // Virtual base class for all controls + class BaseControl + { + public: + BaseControl(olc::QuickGUI::Manager& manager); + virtual ~BaseControl(); + + public: + // Switches the control on/off + void Enable(const bool bEnable); + // Sets whether or not the control is interactive/displayed + bool bVisible = true; + + // True on single frame control begins being manipulated + bool bPressed = false; + // True on all frames control is under user manipulation + bool bHeld = false; + // True on single frame control ceases being manipulated + bool bReleased = false; + + public: + // Updates the controls behvaiour + virtual void Update(olc::PixelGameEngine* pge) = 0; + // Draws the control using "sprite" based CPU operations + virtual void Draw(olc::PixelGameEngine* pge) = 0; + // Draws the control using "decal" based GPU operations + virtual void DrawDecal(olc::PixelGameEngine* pge) = 0; + + protected: + // Controls are related to a manager, where the theme resides + // and control groups can be implemented + olc::QuickGUI::Manager& m_manager; + + // All controls exists in one of four states + // Disabled - Greyed out and not interactive + // Normal - interactive and operational + // Hover - currently under the users mouse focus + // Click - user is interacting with the control + enum class State { Disabled, Normal, Hover, Click } m_state = State::Normal; + + // To add a "swish" to things, controls can fade between states + float m_fTransition = 0.0; + }; + + + // A QuickGUI::Manager acts as a convenient grouping of controls + class Manager + { + public: + // Construct Manager, bCleanUpForMe will automatically DELETE any controls + // given to this manager via AddControl() if true + Manager(const bool bCleanUpForMe = true); + virtual ~Manager(); + + public: + // Add a gui element derived form BaseControl to this manager + void AddControl(BaseControl* control); + // Updates all controls this manager operates + void Update(olc::PixelGameEngine* pge); + // Draws as "sprite" all controls this manager operates + void Draw(olc::PixelGameEngine* pge); + // Draws as "decal" all controls this manager operates + void DrawDecal(olc::PixelGameEngine* pge); + + public: // This managers "Theme" can be set here + // Various element colours + olc::Pixel colNormal = olc::DARK_BLUE; + olc::Pixel colHover = olc::BLUE; + olc::Pixel colClick = olc::CYAN; + olc::Pixel colDisable = olc::DARK_GREY; + olc::Pixel colBorder = olc::WHITE; + olc::Pixel colText = olc::WHITE; + // Speed to transiton from Normal -> Hover + float fHoverSpeedOn = 10.0f; + // Speed to transiton from Hover -> Normal + float fHoverSpeedOff = 4.0f; + // Size of grab handle + float fGrabRad = 8.0f; + // Copy all theme attributes into a different manager object + void CopyThemeFrom(const Manager& manager); + + private: + // Should this manager call delete on the controls it opeerates? + bool m_bEraseControlsOnDestroy = true; + // Container of controls + std::vector m_vControls; + }; + + + // Creates a Label Control - it's just text! + class Label : public BaseControl + { + public: + Label(olc::QuickGUI::Manager& manager, // Associate with a Manager + const std::string& text, // Text to display + const olc::vf2d& pos, // Location of label top-left + const olc::vf2d& size); // Size of label + + public: + // Position of button + olc::vf2d vPos; + // Size of button + olc::vf2d vSize; + // Text displayed on button + std::string sText; + // Show a border? + bool bHasBorder = false; + // Show a background? + bool bHasBackground = false; + // Where should the text be positioned? + enum class Alignment + { + Left, Centre, Right + } nAlign = Alignment::Centre; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + class TextBox : public Label + { + public: + TextBox(olc::QuickGUI::Manager& manager, // Associate with a Manager + const std::string& text, // Text to display + const olc::vf2d& pos, // Location of text box top-left + const olc::vf2d& size); // Size of text box + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + + protected: + bool m_bTextEdit = false; + + }; + + // Creates a Button Control - a clickable, labelled rectangle + class Button : public BaseControl + { + public: + Button(olc::QuickGUI::Manager& manager, // Associate with a Manager + const std::string& text, // Text to display + const olc::vf2d& pos, // Location of button top-left + const olc::vf2d& size); // Size of button + + public: + // Position of button + olc::vf2d vPos; + // Size of button + olc::vf2d vSize; + // Text displayed on button + std::string sText; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + // Creates a Button Control - a clickable, labelled rectangle + class CheckBox : public Button + { + public: + CheckBox(olc::QuickGUI::Manager& manager, // Associate with a Manager + const std::string& text, // Text to display + const bool check, // Is checked or not? + const olc::vf2d& pos, // Location of button top-left + const olc::vf2d& size); // Size of button + + public: + bool bChecked = false; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + class ImageButton : public Button + { + public: + ImageButton(olc::QuickGUI::Manager& manager, // Associate with a Manager + const olc::Renderable& icon, // Text to display + const olc::vf2d& pos, // Location of button top-left + const olc::vf2d& size); // Size of button + + public: + const olc::Renderable& pIcon; + + public: + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + class ImageCheckBox : public ImageButton + { + public: + ImageCheckBox(olc::QuickGUI::Manager& manager, // Associate with a Manager + const olc::Renderable& icon, // Text to display + const bool check, // Is checked or not? + const olc::vf2d& pos, // Location of button top-left + const olc::vf2d& size); // Size of button + + public: + bool bChecked = false; + + public: + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + + // Creates a Slider Control - a grabbable handle that slides between two locations + class Slider : public BaseControl + { + public: + Slider(olc::QuickGUI::Manager& manager, // Associate with a Manager + const olc::vf2d& posmin, // Screen location of "minimum" + const olc::vf2d& posmax, // Screen location of "maximum" + const float valmin, // Value of minimum + const float valmax, // Value of maximum + const float value); // Starting value + + public: + // Minium value + float fMin = -100.0f; + // Maximum value + float fMax = +100.0f; + // Current value + float fValue = 0.0f; + + // Location of minimum/start + olc::vf2d vPosMin; + // Location of maximum/end + olc::vf2d vPosMax; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + + class ListBox : public BaseControl + { + public: + ListBox(olc::QuickGUI::Manager& manager, // Associate with a Manager + std::vector& vList, + const olc::vf2d& pos, // Location of list top-left + const olc::vf2d& size, // Size of list + bool invert); //determines the orientation of text and the slider + + // Position of list + olc::vf2d vPos; + // Size of list + olc::vf2d vSize; + //Go from top to bottom(false) or from bottom to top(true) + bool bInvert; + // Show a border? + bool bHasBorder = true; + // Show a background? + bool bHasBackground = true; + + public: + Slider* m_pSlider = nullptr; + Manager m_group; + size_t m_nVisibleItems = 0; + std::vector& m_vList; + + public: + // Item currently selected + size_t nSelectedItem = 0; + size_t nPreviouslySelectedItem = 0; + // Has selection changed? + bool bSelectionChanged = false; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + + class ModalDialog : public olc::PGEX + { + public: + ModalDialog(); + + public: + void ShowFileOpen(const std::string& sPath); + + protected: + virtual bool OnBeforeUserUpdate(float& fElapsedTime) override; + + private: + bool m_bShowDialog = false; + + Manager m_manFileSelect; + ListBox* m_listVolumes = nullptr; + ListBox* m_listDirectory = nullptr; + ListBox* m_listFiles = nullptr; + + std::vector m_vVolumes; + std::vector m_vDirectory; + std::vector m_vFiles; + std::filesystem::path m_path; + }; +} + + +#ifdef OLC_PGEX_QUICKGUI +#undef OLC_PGEX_QUICKGUI +namespace olc::QuickGUI +{ + +#pragma region BaseControl + BaseControl::BaseControl(olc::QuickGUI::Manager& manager) : m_manager(manager) + { + m_manager.AddControl(this); + } + + BaseControl::~BaseControl() + { + + } + + void BaseControl::Enable(const bool bEnable) + { + m_state = bEnable ? State::Normal : State::Disabled; + } +#pragma endregion + +#pragma region Manager + Manager::Manager(const bool bCleanUpForMe) + { + m_bEraseControlsOnDestroy = bCleanUpForMe; + } + + Manager::~Manager() + { + if (m_bEraseControlsOnDestroy) + for (auto& p : m_vControls) + delete p; + + m_vControls.clear(); + } + + void Manager::AddControl(BaseControl* control) + { + m_vControls.push_back(control); + } + + void Manager::Update(olc::PixelGameEngine* pge) + { + for (auto& p : m_vControls) p->Update(pge); + } + + void Manager::Draw(olc::PixelGameEngine* pge) + { + for (auto& p : m_vControls) p->Draw(pge); + } + + void Manager::DrawDecal(olc::PixelGameEngine* pge) + { + for (auto& p : m_vControls) p->DrawDecal(pge); + } + + void Manager::CopyThemeFrom(const Manager& manager) + { + this->colBorder = manager.colBorder; + this->colClick = manager.colClick; + this->colDisable = manager.colDisable; + this->colHover = manager.colHover; + this->colNormal = manager.colNormal; + this->colText = manager.colText; + this->fGrabRad = manager.fGrabRad; + this->fHoverSpeedOff = manager.fHoverSpeedOff; + this->fHoverSpeedOn = manager.fHoverSpeedOn; + } +#pragma endregion + +#pragma region Label + Label::Label(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) + : BaseControl(manager) + { + vPos = pos; vSize = size; sText = text; + } + + void Label::Update(olc::PixelGameEngine* pge) + { + + } + + void Label::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + + olc::vf2d vText = pge->GetTextSizeProp(sText); + switch (nAlign) + { + case Alignment::Left: + pge->DrawStringProp(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), sText, m_manager.colText); + break; + case Alignment::Centre: + pge->DrawStringProp(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + break; + case Alignment::Right: + pge->DrawStringProp(olc::vf2d{ vPos.x + vSize.x - vText.x - 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); + break; + } + } + + void Label::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + olc::vf2d vText = pge->GetTextSizeProp(sText); + switch (nAlign) + { + case Alignment::Left: + pge->DrawStringPropDecal({ vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); + break; + case Alignment::Centre: + pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + break; + case Alignment::Right: + pge->DrawStringPropDecal({ vPos.x + vSize.x - vText.x - 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f }, sText, m_manager.colText); + break; + } + } +#pragma endregion + + +#pragma region TextBox + TextBox::TextBox(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) + : Label(manager, text, pos, size) + { + nAlign = Alignment::Left; + bHasBorder = true; + bHasBackground = false; + } + + void TextBox::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + bPressed = false; + bReleased = false; + + olc::vf2d vMouse = pge->GetMousePos(); + + if (vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && + vMouse.y >= vPos.y && vMouse.y < vPos.y + vSize.y) + { + // Released inside box does nothing to me, but i may have + // to finish off the neighbours... oo err + bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; + bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; + + if (bPressed && pge->IsTextEntryEnabled() && !m_bTextEdit) + { + pge->TextEntryEnable(false); + } + + + if (bPressed && !pge->IsTextEntryEnabled() && !m_bTextEdit) + { + pge->TextEntryEnable(true, sText); + m_bTextEdit = true; + } + + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; + + + } + else + { + // Released outside box + bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; + bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; + + if (bPressed && m_bTextEdit) + { + sText = pge->TextEntryGetString(); + pge->TextEntryEnable(false); + m_bTextEdit = false; + } + } + + if (m_bTextEdit && pge->IsTextEntryEnabled()) + sText = pge->TextEntryGetString(); + } + + void TextBox::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + + if (m_bTextEdit && pge->IsTextEntryEnabled()) + { + // Draw Cursor + int32_t i = pge->TextEntryGetCursor(); + olc::vf2d vCursorPos = pge->GetTextSizeProp(sText.substr(0, i)); + pge->FillRect(olc::vf2d(vPos.x + 2.0f + vCursorPos.x, (vPos.y + (vSize.y - 10.0f) * 0.5f)), { 2, 10 }, m_manager.colText); + } + + // Draw Text + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringProp(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), sText, m_manager.colText); + + } + + void TextBox::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + if (m_bTextEdit && pge->IsTextEntryEnabled()) + { + // Draw Cursor + int32_t i = pge->TextEntryGetCursor(); + olc::vf2d vCursorPos = pge->GetTextSizeProp(sText.substr(0, i)); + pge->FillRectDecal(olc::vf2d(vPos.x + 2.0f + vCursorPos.x, (vPos.y + (vSize.y - 10.0f) * 0.5f)), { 2, 10 }, m_manager.colText); + } + + // Draw Text + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringPropDecal(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), sText, m_manager.colText); + } +#pragma endregion + +#pragma region Button + Button::Button(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size) + : BaseControl(manager) + { + vPos = pos; vSize = size; sText = text; + } + + void Button::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + bPressed = false; + bReleased = false; + float fElapsedTime = pge->GetElapsedTime(); + + olc::vf2d vMouse = pge->GetMousePos(); + if (m_state != State::Click) + { + if (vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && + vMouse.y >= vPos.y && vMouse.y < vPos.y + vSize.y) + { + m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; + m_state = State::Hover; + + bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed; + if (bPressed) + { + m_state = State::Click; + } + + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; + } + else + { + m_fTransition -= fElapsedTime * m_manager.fHoverSpeedOff; + m_state = State::Normal; + } + } + else + { + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld; + bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased; + if (bReleased) m_state = State::Normal; + } + + m_fTransition = std::clamp(m_fTransition, 0.0f, 1.0f); + } + + void Button::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + switch (m_state) + { + case State::Disabled: + pge->FillRect(vPos, vSize, m_manager.colDisable); + break; + case State::Normal: + case State::Hover: + pge->FillRect(vPos, vSize, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + break; + case State::Click: + pge->FillRect(vPos, vSize, m_manager.colClick); + break; + } + + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringProp(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + } + + void Button::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + switch (m_state) + { + case State::Disabled: + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colDisable); + break; + case State::Normal: + case State::Hover: + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + break; + case State::Click: + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colClick); + break; + } + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + } +#pragma endregion + + +#pragma region ImageButton + ImageButton::ImageButton(olc::QuickGUI::Manager& manager, const olc::Renderable& icon, const olc::vf2d& pos, const olc::vf2d& size) + : Button(manager, "", pos, size), pIcon(icon) + { + + } + + void ImageButton::Draw(olc::PixelGameEngine* pge) + { + Button::Draw(pge); + pge->DrawSprite(vPos + olc::vi2d(4, 4), pIcon.Sprite()); + } + + void ImageButton::DrawDecal(olc::PixelGameEngine* pge) + { + Button::DrawDecal(pge); + pge->DrawDecal(vPos + olc::vi2d(4, 4), pIcon.Decal()); + } +#pragma endregion + + +#pragma region ImageCheckBox + ImageCheckBox::ImageCheckBox(olc::QuickGUI::Manager& manager, const olc::Renderable& gfx, const bool check, const olc::vf2d& pos, const olc::vf2d& size) + : ImageButton(manager, gfx, pos, size) + { + bChecked = check; + } + + void ImageCheckBox::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + ImageButton::Update(pge); + if (bPressed) bChecked = !bChecked; + } + + void ImageCheckBox::Draw(olc::PixelGameEngine* pge) + { + ImageButton::Draw(pge); + + if (bChecked) + pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(5, 5), m_manager.colBorder); + } + + void ImageCheckBox::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + ImageButton::DrawDecal(pge); + + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(2, 2), vSize - olc::vf2d(4, 4), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + } +#pragma endregion + + +#pragma region CheckBox + CheckBox::CheckBox(olc::QuickGUI::Manager& manager, const std::string& text, const bool check, const olc::vf2d& pos, const olc::vf2d& size) + : Button(manager, text, pos, size) + { + bChecked = check; + } + + void CheckBox::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + Button::Update(pge); + if (bPressed) + bChecked = !bChecked; + } + + void CheckBox::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + Button::Draw(pge); + + if (bChecked) + pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(5, 5), m_manager.colBorder); + } + + void CheckBox::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + Button::DrawDecal(pge); + + if (bChecked) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(2, 2), vSize - olc::vf2d(4, 4), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + olc::vf2d vText = pge->GetTextSizeProp(sText); + pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText); + } +#pragma endregion + +#pragma region Slider + Slider::Slider(olc::QuickGUI::Manager& manager, const olc::vf2d& posmin, const olc::vf2d& posmax, const float valmin, const float valmax, const float value) + : BaseControl(manager) + { + vPosMin = posmin; vPosMax = posmax; fMin = valmin; fMax = valmax; fValue = value; + } + + void Slider::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + float fElapsedTime = pge->GetElapsedTime(); + + olc::vf2d vMouse = pge->GetMousePos(); + bHeld = false; + if (m_state == State::Click) + { + olc::vf2d d = vPosMax - vPosMin; + float u = d.dot(vMouse - vPosMin) / d.mag2(); + fValue = u * (fMax - fMin) + fMin; + bHeld = true; + } + else + { + olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); + if ((vMouse - vSliderPos).mag2() <= int32_t(m_manager.fGrabRad) * int32_t(m_manager.fGrabRad)) + { + m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; + m_state = State::Hover; + if (pge->GetMouse(olc::Mouse::LEFT).bPressed) + { + m_state = State::Click; + bPressed = true; + } + } + else + m_state = State::Normal; + } + + if (pge->GetMouse(olc::Mouse::LEFT).bReleased) + { + m_state = State::Normal; + bReleased = true; + } + + if (m_state == State::Normal) + { + m_fTransition -= fElapsedTime * m_manager.fHoverSpeedOff; + m_state = State::Normal; + bHeld = false; + } + + fValue = std::clamp(fValue, fMin, fMax); + m_fTransition = std::clamp(m_fTransition, 0.0f, 1.0f); + } + + void Slider::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + pge->DrawLine(vPosMin, vPosMax, m_manager.colBorder); + olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); + + switch (m_state) + { + case State::Disabled: + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colDisable); + break; + case State::Normal: + case State::Hover: + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + break; + case State::Click: + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colClick); + break; + } + + + pge->DrawCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colBorder); + } + + void Slider::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + pge->DrawLineDecal(vPosMin, vPosMax, m_manager.colBorder); + olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); + + switch (m_state) + { + case State::Disabled: + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colDisable); + break; + case State::Normal: + case State::Hover: + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + break; + case State::Click: + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colClick); + break; + } + + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + +#pragma endregion + +#pragma region ListBox + ListBox::ListBox(olc::QuickGUI::Manager& manager, std::vector& vList, const olc::vf2d& pos, const olc::vf2d& size, bool invert) + : BaseControl(manager), m_vList(vList) + { + m_group.CopyThemeFrom(m_manager); + vPos = pos; + vSize = size; + bInvert = invert; + float yValueMin, yValueMax; + double sliderMaxVal, sliderInitVal; + if (bInvert) { + yValueMin = pos.y - m_manager.fGrabRad - 1 + size.y; + yValueMax = pos.y + m_manager.fGrabRad + 1; + sliderMaxVal = 0; + sliderInitVal = float(m_vList.size()); + } + else { + yValueMin = pos.y + m_manager.fGrabRad + 1; + yValueMax = pos.y + size.y - m_manager.fGrabRad - 1; + sliderMaxVal = float(m_vList.size()); + sliderInitVal = 0; + } + m_pSlider = new Slider(m_group, { pos.x + size.x - m_manager.fGrabRad - 1, yValueMin }, + { pos.x + size.x - m_manager.fGrabRad - 1, yValueMax }, 0, sliderMaxVal, sliderInitVal); + } + + void ListBox::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + + nPreviouslySelectedItem = nSelectedItem; + olc::vf2d vMouse = pge->GetMousePos() - vPos + olc::vi2d(2, 0); + if (pge->GetMouse(olc::Mouse::LEFT).bPressed) + { + if (vMouse.x >= 0 && vMouse.x < vSize.x - (m_group.fGrabRad * 2) && vMouse.y >= 0 && vMouse.y < vSize.y) + { + float sliderOffSet = (bInvert) ? m_pSlider->fValue : m_pSlider->fValue; + float mouseYPosAdjusted = (bInvert) ? ((m_pSlider->vPosMin.y - 25) - vMouse.y) / 10 : (vMouse.y / 10); + nSelectedItem = size_t(mouseYPosAdjusted + sliderOffSet); + } + } + + nSelectedItem = std::clamp(nSelectedItem, size_t(0), m_vList.size() - 1); + + bSelectionChanged = nSelectedItem != nPreviouslySelectedItem; + + + m_pSlider->fMax = float(m_vList.size()); + m_group.Update(pge); + } + + //Notes for c8080 version of this + //This method so that the ListBox shows the oldest entries in the vector at the top. The newest at the bottom + //This is to resemble an actual commandLine + void ListBox::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + + + size_t idx0 = size_t(m_pSlider->fValue); + size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); + + //for invert + olc::vf2d vTextAdjust, vTextPos; + if (bInvert) { + vTextAdjust = { 0, vSize.y - 15 }; + } + else { + vTextAdjust = { 0, 0 }; + } + vTextPos = vPos + olc::vf2d(2, 2) + vTextAdjust; + int yPositionDelta = (bInvert) ? -10 : 10; + for (size_t idx = idx0; idx < idx1; idx++) + { + if (idx == nSelectedItem) + pge->FillRect(vTextPos - olc::vi2d(1, 1), { int32_t(vSize.x - m_group.fGrabRad * 2), 10 }, m_group.colHover); + pge->DrawStringProp(vTextPos, m_vList[(bInvert) ? (m_vList.size() - 1) - idx : idx]); + vTextPos.y += yPositionDelta; + } + + m_group.Draw(pge); + } + + void ListBox::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + + size_t idx0 = size_t(m_pSlider->fValue); + size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); + + //for invert + olc::vf2d vTextAdjust, vTextPos; + if (bInvert) { + vTextAdjust = { 0, vSize.y - 15 }; + } + else { + vTextAdjust = { 0, 0 }; + } + vTextPos = vPos + olc::vf2d(2, 2) + vTextAdjust; + int yPositionDelta = (bInvert) ? -10 : 10; + + for (size_t idx = idx0; idx < idx1; idx++) + { + if (idx == nSelectedItem) + pge->FillRectDecal(vTextPos - olc::vi2d(1, 1), { vSize.x - m_group.fGrabRad * 2.0f, 10.0f }, m_group.colHover); + pge->DrawStringPropDecal(vTextPos, m_vList[(bInvert) ? (m_vList.size() - 1) - idx : idx]); + vTextPos.y += yPositionDelta; + } + + if (bHasBorder) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + m_group.DrawDecal(pge); + } +#pragma endregion + + + +#pragma region Modal + ModalDialog::ModalDialog() : olc::PGEX(true) + { + + // Create File Open Dialog + olc::vi2d vScreenSize = pge->GetScreenSize(); + + m_listDirectory = new ListBox(m_manFileSelect, m_vDirectory, olc::vf2d(20, 20), olc::vf2d(300, 500), false); + m_listFiles = new ListBox(m_manFileSelect, m_vFiles, olc::vf2d(330, 20), olc::vf2d(300, 500), false); + + m_path = "/"; + for (auto const& dir_entry : std::filesystem::directory_iterator{ m_path }) + { + if (dir_entry.is_directory()) + m_vDirectory.push_back(dir_entry.path().filename().string()); + else + m_vFiles.push_back(dir_entry.path().filename().string()); + } + } + + void ModalDialog::ShowFileOpen(const std::string& sPath) + { + m_bShowDialog = true; + } + + bool ModalDialog::OnBeforeUserUpdate(float& fElapsedTime) + { + if (!m_bShowDialog) return false; + + m_manFileSelect.Update(this->pge); + + if (pge->GetKey(olc::Key::BACK).bPressed) + { + m_path = m_path.parent_path().string() + "/"; + //m_listDirectory->bSelectionChanged = true; + //m_listDirectory->nSelectedItem = 0; + } + + if (m_listDirectory->bSelectionChanged) + { + std::string sDirectory = m_vDirectory[m_listDirectory->nSelectedItem]; + /*if (sDirectory == "..") + m_path = m_path.parent_path().string() + "/"; + else + m_path += sDirectory+ "/";*/ + + + m_path += sDirectory + "/"; + // Reconstruct Lists + m_vDirectory.clear(); + + m_vFiles.clear(); + + + for (auto const& dir_entry : std::filesystem::directory_iterator{ m_path }) + { + if (dir_entry.is_directory() || dir_entry.is_other()) + m_vDirectory.push_back(dir_entry.path().filename().string()); + else + m_vFiles.push_back(dir_entry.path().filename().string()); + } + + //m_vDirectory.push_back(".."); + + //m_listFiles->nSelectedItem = 0; + //m_listDirectory->nSelectedItem = 0; + + } + + pge->DrawStringDecal({ 0,0 }, m_path.string()); + + + + + m_manFileSelect.DrawDecal(this->pge); + + + + if (pge->GetKey(olc::Key::ESCAPE).bPressed) + { + m_bShowDialog = false; + return false; + } + + return true; + } +#pragma endregion + +} +#endif // OLC_PGEX_QUICKGUI +#endif // OLC_PGEX_QUICKGUI_H From 14e8000a847dc3bb1aab9197dd51f6cdcc120625 Mon Sep 17 00:00:00 2001 From: Louis <78240770+LouisStefanski-dev@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:28:41 -0700 Subject: [PATCH 2/2] Set default argument for invert to false This allows for codebases to be updated with no code modification --- extensions/olcPGEX_QuickGUI.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/olcPGEX_QuickGUI.h b/extensions/olcPGEX_QuickGUI.h index e4810d25..df894554 100644 --- a/extensions/olcPGEX_QuickGUI.h +++ b/extensions/olcPGEX_QuickGUI.h @@ -48,7 +48,7 @@ Homepage: https://www.onelonecoder.com Author ~~~~~~ - David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 + David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022 Changes ~~~~~~~ v1.01 +Moved Slider::fGrabRad into "theme" @@ -974,7 +974,7 @@ namespace olc::QuickGUI #pragma endregion #pragma region ListBox - ListBox::ListBox(olc::QuickGUI::Manager& manager, std::vector& vList, const olc::vf2d& pos, const olc::vf2d& size, bool invert) + ListBox::ListBox(olc::QuickGUI::Manager& manager, std::vector& vList, const olc::vf2d& pos, const olc::vf2d& size, bool invert = false) : BaseControl(manager), m_vList(vList) { m_group.CopyThemeFrom(m_manager);