From 94e85f1a9a07bea279642750f823cd409c5edca3 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sat, 12 Aug 2023 22:53:13 -0500 Subject: [PATCH] Update QuickGUI to handle drawing and updating offsets. Textbox now follows cursor within dimension contraints. --- FiestaOnlineEditor/FiestaOnlineEditor.cpp | 2 + FiestaOnlineEditor/FiestaOnlineEditor.vcxproj | 1 + .../FiestaOnlineEditor.vcxproj.filters | 3 + FiestaOnlineEditor/ItemEditor.cpp | 23 +- FiestaOnlineEditor/ItemEditor.h | 5 +- FiestaOnlineEditor/Window.cpp | 4 + FiestaOnlineEditor/Window.h | 3 +- FiestaOnlineEditor/olcPGEX_QuickGUI.h | 1332 +++++++++++++++++ 8 files changed, 1354 insertions(+), 19 deletions(-) create mode 100644 FiestaOnlineEditor/olcPGEX_QuickGUI.h diff --git a/FiestaOnlineEditor/FiestaOnlineEditor.cpp b/FiestaOnlineEditor/FiestaOnlineEditor.cpp index 39e4ee9..b4ff277 100644 --- a/FiestaOnlineEditor/FiestaOnlineEditor.cpp +++ b/FiestaOnlineEditor/FiestaOnlineEditor.cpp @@ -6,6 +6,8 @@ #include "SHNFileDecryptor.h" #define OLC_PGEX_TRANSFORMEDVIEW #include "olcPGEX_TransformedView.h" +#define OLC_PGEX_QUICKGUI +#include "olcPGEX_QuickGUI.h" #include "FiestaOnlineEditor.h" #include "ActionIDs.h" #include "ItemEditor.h" diff --git a/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj b/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj index 9675825..b7f5dec 100644 --- a/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj +++ b/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj @@ -135,6 +135,7 @@ + diff --git a/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj.filters b/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj.filters index cc8bee7..f95f99a 100644 --- a/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj.filters +++ b/FiestaOnlineEditor/FiestaOnlineEditor.vcxproj.filters @@ -48,6 +48,9 @@ Header Files + + Header Files + diff --git a/FiestaOnlineEditor/ItemEditor.cpp b/FiestaOnlineEditor/ItemEditor.cpp index ecf704e..1197ab2 100644 --- a/FiestaOnlineEditor/ItemEditor.cpp +++ b/FiestaOnlineEditor/ItemEditor.cpp @@ -120,33 +120,22 @@ void ItemEditor::Load(std::string basePath,std::string clientPath,std::string im it.stat_dmg=GradeItemOption.Get(i,14); it.stat_magDmg=GradeItemOption.Get(i,15); } + + //QuickGUI is magic and handles freeing memory for us! Don't need to! + searchBox=new QuickGUI::TextBox(manager,"",vf2d{8,16},vf2d{128,26},vf2d{1,2}); } void ItemEditor::Update(FiestaOnlineEditor*pge,float fElapsedTime){ bool updateRequired=false; - if(pge->GetKey(UP).bHeld){ - pos.y-=32*fElapsedTime; - updateRequired=true; - } - if(pge->GetKey(DOWN).bHeld){ - pos.y+=32*fElapsedTime; - updateRequired=true; - } - if(pge->GetKey(RIGHT).bHeld){ - pos.x+=32*fElapsedTime; - updateRequired=true; - } - if(pge->GetKey(LEFT).bHeld){ - pos.x-=32*fElapsedTime; - updateRequired=true; - } + updateRequired=true; + manager.Update(pge,GetPos()); if(updateRequired){ InternalRefresh(pge); } } void ItemEditor::Refresh(FiestaOnlineEditor*pge){ - pge->DrawRect(pos,{16,16},WHITE); + manager.Draw(pge,vi2d{0,headerHeight}); } void ItemEditor::MouseFocus(FiestaOnlineEditor*pge){ diff --git a/FiestaOnlineEditor/ItemEditor.h b/FiestaOnlineEditor/ItemEditor.h index 1a65aef..eef4f57 100644 --- a/FiestaOnlineEditor/ItemEditor.h +++ b/FiestaOnlineEditor/ItemEditor.h @@ -1,5 +1,6 @@ #pragma once #include "olcPGEX_TransformedView.h" +#include "olcPGEX_QuickGUI.h" class FiestaOnlineEditor; @@ -158,10 +159,12 @@ public: class ItemEditor:public Window{ SHNFile ItemInfo,ItemInfoServer,ItemViewInfo,GradeItemOption; - vf2d pos={64,64}; + //vf2d pos={64,64}; std::map>imgLookup; std::mapitemList; bool reloadImages; + QuickGUI::Manager manager; + QuickGUI::TextBox*searchBox; public: ItemEditor(FiestaOnlineEditor*pge,std::string windowTitle,vi2d pos,vi2d size,bool reloadImages=true); ~ItemEditor(); diff --git a/FiestaOnlineEditor/Window.cpp b/FiestaOnlineEditor/Window.cpp index dc4b76f..7c066c0 100644 --- a/FiestaOnlineEditor/Window.cpp +++ b/FiestaOnlineEditor/Window.cpp @@ -122,4 +122,8 @@ bool Window::IsClosed(){ void Window::SetFocusedWindow(FiestaOnlineEditor*pge,Window*w){ focusedWindow=w; w->InternalRefresh(pge); +} + +vf2d Window::GetPos(){ + return pos+vi2d{0,headerHeight}; } \ No newline at end of file diff --git a/FiestaOnlineEditor/Window.h b/FiestaOnlineEditor/Window.h index 2493ec9..519c52c 100644 --- a/FiestaOnlineEditor/Window.h +++ b/FiestaOnlineEditor/Window.h @@ -7,16 +7,17 @@ class Window{ private: vi2d pos; Decal*decWindow; - const int headerHeight=12; bool dragging=false; vf2d dragPoint; bool canClose=false; bool closed=false; protected: + const int headerHeight=12; vi2d size; Sprite*sprWindow; static Window*focusedWindow; std::string windowTitle=""; + vf2d GetPos(); //Note this isn't the actual window's position, it's the drawable window position. public: Window(FiestaOnlineEditor*pge,std::string windowTitle,vi2d pos,vi2d size); virtual ~Window(); diff --git a/FiestaOnlineEditor/olcPGEX_QuickGUI.h b/FiestaOnlineEditor/olcPGEX_QuickGUI.h new file mode 100644 index 0000000..017eaee --- /dev/null +++ b/FiestaOnlineEditor/olcPGEX_QuickGUI.h @@ -0,0 +1,1332 @@ +/* +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; + // true on all frames control is being hovered over + bool bHovered = false; + + public: + // Updates the controls behvaiour + virtual void Update(olc::PixelGameEngine* pge,vf2d offsetPos={0,0}) = 0; + // Draws the control using "sprite" based CPU operations + virtual void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) = 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,vf2d offsetPos={0,0}); + // Draws as "sprite" all controls this manager operates + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}); + // 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 = {45, 0, 105}; + olc::Pixel colSelected = {183, 99, 201}; + olc::Pixel colHover = {173, 127, 235}; + olc::Pixel colClick = {129, 36, 255}; + olc::Pixel colDisable = {93, 87, 102}; + olc::Pixel colBorder = {209, 209, 209}; + olc::Pixel colBorderSelected = {63, 35, 84}; + olc::Pixel colText = {209, 209, 209}; + // 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,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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, + const olc::vf2d& fontSize={1,1}); // Size of text box + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + bool m_bTextEdit = false; + + protected: + vf2d fontSize; + }; + + // 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, + const olc::vf2d& fontScale={1,1}); // Size of button + + public: + // Position of button + olc::vf2d vPos; + // Size of button + olc::vf2d vSize; + // Text displayed on button + std::string sText; + olc::vf2d fontScale; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; + + class CustomButton : public Button + { + public: + CustomButton(olc::QuickGUI::Manager& manager, // Associate with a Manager + const olc::Renderable &icon, // Text to display + const olc::Renderable &back_icon, // The image that appears behind the button, to be blended. + const olc::vf2d& pos, // Location of button top-left + const olc::vf2d& size, + const olc::vi2d& sprOffset, + const olc::vi2d& sprSize); // Size of button + + public: + const olc::Renderable& pIcon; + const olc::Renderable& pBackIcon; + olc::vi2d sprOffset; + olc::vi2d sprSize; + + public: + 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, + const olc::vi2d& sprOffset, + const olc::vi2d& sprSize); // Size of button + + public: + const olc::Renderable& pIcon; + olc::vi2d sprOffset; + olc::vi2d sprSize; + + public: + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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, + const olc::vi2d& sprOffset, + const olc::vi2d& sprSize); // Size of button + + public: + bool bChecked = false; + + public: + void Update(olc::PixelGameEngine* pge,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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, // Location of list top-left + const float fontSize=10); // 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; + float fontSize=10; + + 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,vf2d offsetPos={0,0}) override; + void Draw(olc::PixelGameEngine* pge,vf2d drawOffset={0,0}) 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,vf2d offsetPos) + { + for (auto& p : m_vControls) p->Update(pge,offsetPos); + } + + void Manager::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + for (auto& p : m_vControls) p->Draw(pge,drawOffset); + } + + 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,vf2d offsetPos) + { + + } + + void Label::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPos=this->vPos+drawOffset; + + 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,const olc::vf2d& fontSize) + : Label(manager, text, pos, size),fontSize(fontSize) + { + nAlign = Alignment::Left; + bHasBorder = true; + bHasBackground = false; + } + + void TextBox::Update(olc::PixelGameEngine* pge,vf2d offsetPos) + { + if (m_state == State::Disabled || !bVisible) + return; + + bPressed = false; + bReleased = false; + + olc::vf2d vMouse = pge->GetMousePos(); + + + if (m_bTextEdit && !pge->IsTextEntryEnabled()){ + //sText = pge->TextEntryGetString(); + m_bTextEdit = false; + } + + vf2d vPos=this->vPos+offsetPos; + + 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 && pge->IsTextEntryEnabled()) + { + sText = pge->TextEntryGetString(); + pge->TextEntryEnable(false); + m_bTextEdit = false; + } + } + + if (m_bTextEdit && pge->IsTextEntryEnabled()) + sText = pge->TextEntryGetString(); + } + + void TextBox::Draw(olc::PixelGameEngine* pge, vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPos=this->vPos+drawOffset; + + if (bHasBackground) + { + pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + float fontScale=std::max(fontSize.x,fontSize.y); //Because CPU rendering does not have separate axis scaling. + + if (bHasBorder) + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + + // Draw Text + int marker=sText.length(); + std::string cutText; + while(marker>0){ + marker--; + if(pge->GetTextSizeProp(sText.substr(marker)).x*fontScale>vSize.x){ + break; + } else { + cutText=sText.substr(marker); + } + } + + if (m_bTextEdit && pge->IsTextEntryEnabled()) + { + // Draw Cursor + int32_t i = pge->TextEntryGetCursor(); + olc::vf2d vCursorPos = pge->GetTextSizeProp(cutText.substr(0, i))*fontScale; + pge->FillRect(olc::vf2d(vPos.x + 2.0f + vCursorPos.x, (vPos.y + (vSize.y - 10.0f) * 0.5f)), { 2, int(fontScale)*8+2 }, m_manager.colText); + } + + olc::vf2d vText = pge->GetTextSizeProp(cutText); + pge->DrawStringProp(olc::vf2d(vPos.x + 2.0f, vPos.y + (vSize.y - vText.y) * 0.5f), cutText, m_manager.colText, fontScale); + + } + + 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))*fontSize; + 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)-vf2d{0,1*fontSize.y}, sText, m_manager.colText,fontSize); + } +#pragma endregion + +#pragma region Button + Button::Button(olc::QuickGUI::Manager& manager, const std::string& text, const olc::vf2d& pos, const olc::vf2d& size, const olc::vf2d& fontScale) + : BaseControl(manager),fontScale(fontScale) + { + vPos = pos; vSize = size; sText = text; + } + + void Button::Update(olc::PixelGameEngine* pge,vf2d offsetPos) + { + 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) + { + vf2d vPos=this->vPos+offsetPos; + 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; + bHovered = true; + + 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; + bHovered = false; + } + } + 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, vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPos=this->vPos+drawOffset; + + 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)*fontScale; + pge->DrawStringPropDecal(vPos + (vSize - vText) * 0.5f, sText, m_manager.colText,fontScale); + } +#pragma endregion + +#pragma region CustomButton + CustomButton::CustomButton(olc::QuickGUI::Manager& manager, const olc::Renderable& icon,const olc::Renderable& back_icon, const olc::vf2d& pos, const olc::vf2d& size,const olc::vi2d& sprOffset,const olc::vi2d& sprSize) + : Button(manager, "", pos, size), pIcon(icon), pBackIcon(back_icon), sprOffset(sprOffset), sprSize(sprSize) + {} + + void CustomButton::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + switch (m_state) + { + case State::Disabled: + pge->DrawPartialDecal(vPos,pBackIcon.Decal(),sprOffset,sprSize,{1.0,1.0},m_manager.colDisable); + break; + case State::Normal: + case State::Hover: + pge->DrawPartialDecal(vPos,pBackIcon.Decal(),sprOffset,sprSize,{1.0,1.0},olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + break; + case State::Click: + pge->DrawPartialDecal(vPos,pBackIcon.Decal(),sprOffset,sprSize,{1.0,1.0},m_manager.colClick); + break; + } + pge->DrawPartialDecal(vPos,pIcon.Decal(),sprOffset,sprSize,{1.0,1.0},WHITE); + } +#pragma endregion + + +#pragma region ImageButton + ImageButton::ImageButton(olc::QuickGUI::Manager& manager, const olc::Renderable& icon, const olc::vf2d& pos, const olc::vf2d& size,const olc::vi2d& sprOffset,const olc::vi2d& sprSize) + : Button(manager, "", pos, size), pIcon(icon), sprOffset(sprOffset), sprSize(sprSize) + { + } + + void ImageButton::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + Button::Draw(pge,drawOffset); + vf2d vPos=this->vPos+drawOffset; + pge->DrawPartialSprite(vPos + olc::vi2d(4, 4), pIcon.Sprite(),sprOffset,sprSize); + } + + void ImageButton::DrawDecal(olc::PixelGameEngine* pge) + { + Button::DrawDecal(pge); + pge->DrawPartialDecal(vPos + olc::vi2d(4, 4), pIcon.Decal(),sprOffset,sprSize); + } +#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,const olc::vi2d& sprOffset,const olc::vi2d& sprSize) + : ImageButton(manager, gfx, pos, size,sprOffset,sprSize) + { + bChecked = check; + } + + void ImageCheckBox::Update(olc::PixelGameEngine* pge,vf2d offsetPos) + { + if (m_state == State::Disabled || !bVisible) + return; + + ImageButton::Update(pge); + if (bPressed) bChecked = !bChecked; + } + + void ImageCheckBox::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + ImageButton::Draw(pge,drawOffset); + + vf2d vPos=this->vPos+drawOffset; + 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; + + + + 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); + ImageButton::DrawDecal(pge); + } + + 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: + if(bChecked){ + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), olc::PixelLerp(m_manager.colSelected, m_manager.colHover, m_fTransition)); + } else { + 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->DrawPartialDecal(vPos + olc::vi2d(4, 4), pIcon.Decal(),sprOffset,sprSize); + + + 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 (bChecked) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + for(int i=0;i<4;i++){ + pge->FillRectDecal(vPos + olc::vf2d(2, 2) + olc::vf2d(i,i)/2, vSize - olc::vf2d(4, 4) - olc::vf2d(i*2,i*2)/2, m_manager.colBorderSelected); + } + 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,vf2d offsetPos) + { + if (m_state == State::Disabled || !bVisible) + return; + + Button::Update(pge); + if (bPressed) + bChecked = !bChecked; + } + + void CheckBox::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPos=this->vPos+drawOffset; + + Button::Draw(pge,drawOffset); + + 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,vf2d offsetPos) + { + if (m_state == State::Disabled || !bVisible) + return; + + float fElapsedTime = pge->GetElapsedTime(); + + olc::vf2d vMouse = pge->GetMousePos(); + bHeld = false; + vf2d vPosMax = this->vPosMax; + vf2d vPosMin = this->vPosMin; + 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; + bHovered = true; + if (pge->GetMouse(olc::Mouse::LEFT).bPressed) + { + m_state = State::Click; + bPressed = true; + } + } + else { + m_state = State::Normal; + bHovered = false; + } + } + + 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,vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPosMin=this->vPosMin+drawOffset; + vf2d vPosMax=this->vPosMax+drawOffset; + + 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, const float fontSize) + : BaseControl(manager), m_vList(vList), fontSize(fontSize) + { + 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,vf2d offsetPos) + { + if (m_state == State::Disabled || !bVisible) + return; + + nPreviouslySelectedItem = nSelectedItem; + olc::vf2d vMouse = pge->GetMousePos() - vPos + olc::vi2d(2,0); + + vf2d vPos=this->vPos+offsetPos; + bHovered=pge->GetMouseX() >= vPos.x && pge->GetMouseX() < vPos.x + vSize.x && + pge->GetMouseY() >= vPos.y && pge->GetMouseY() < vPos.y + vSize.y; + + if (pge->GetMouse(olc::Mouse::LEFT).bHeld) + { + 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 / fontSize); + } + } + + nSelectedItem = std::clamp(nSelectedItem, size_t(0), m_vList.size()-1); + + bSelectionChanged = nSelectedItem != nPreviouslySelectedItem; + + + m_pSlider->fMax = float(m_vList.size()-vSize.y/fontSize+1); + + if(bHovered&&pge->GetMouseWheel()!=0){ + if(pge->GetMouseWheel()>0){ + m_pSlider->fValue=std::clamp(m_pSlider->fValue-1, m_pSlider->fMin, m_pSlider->fMax); + } else { + m_pSlider->fValue=std::clamp(m_pSlider->fValue+1, m_pSlider->fMin, m_pSlider->fMax); + } + } + + m_pSlider->fValue=int(m_pSlider->fValue); + + m_group.Update(pge); + } + + void ListBox::Draw(olc::PixelGameEngine* pge,vf2d drawOffset) + { + if (!bVisible) + return; + + vf2d vPos=this->vPos+drawOffset; + + 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) / fontSize), 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), int(fontSize)}, m_group.colHover); + pge->DrawStringProp(vTextPos + olc::vi2d(0,2), m_vList[idx],olc::WHITE,fontSize/10); + vTextPos.y += fontSize; + } + + 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) / fontSize), 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, fontSize }, m_group.colHover); + float width = pge->GetTextSizeProp(m_vList[idx]).x*fontSize/10; + if (width>vSize.x-m_manager.fGrabRad*2){ + float scaleX = (vSize.x-m_manager.fGrabRad*2)/width; + pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,2), m_vList[idx], olc::WHITE, olc::vf2d{scaleX,1}*fontSize/10); + } else { + pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,2), m_vList[idx], olc::WHITE, olc::vf2d{1,1}*fontSize/10); + } + vTextPos.y += fontSize; + } + + 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