mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #6721
6721: Improve selection popups and dropdown menus, add dropdown menus to select screen and active controller r=def- a=Robyt3 Screenshots: - Controls settings: - Before: ![controls old](https://github.com/ddnet/ddnet/assets/23437060/8907c38a-d483-4766-84ad-49464b9760a0) - After: ![controls new](https://github.com/ddnet/ddnet/assets/23437060/f021e961-ce90-4557-aef0-a1e1a4bc7ca4) - Graphics settings (collapsed): - Before: ![graphics_collapsed old](https://github.com/ddnet/ddnet/assets/23437060/d895c8d8-a72f-4da2-aa57-93d5f08be648) - After: ![graphics_collapsed new](https://github.com/ddnet/ddnet/assets/23437060/e594a599-1210-4ba0-87f2-b2a824043dc4) - Graphics settings (expanded): - Before: ![graphics_expanded old](https://github.com/ddnet/ddnet/assets/23437060/cce1d8cb-4dff-4d43-a9f0-af2dbfeff27d) - After: ![graphics_expanded_1 new](https://github.com/ddnet/ddnet/assets/23437060/c99c24cb-3398-4a80-9b99-3281ed62f6a7) ![graphics_expanded_2 new](https://github.com/ddnet/ddnet/assets/23437060/83fc2522-0da6-40d8-ad2b-3e3159b74308) ![graphics_expanded_3 new](https://github.com/ddnet/ddnet/assets/23437060/b00b30c4-411b-43a0-97f0-ecd47ebea516) - The maximum dropdown menu height is limited to 40% of the screen height. The content will begin to scroll if this is exceeded: ![scrolling](https://github.com/ddnet/ddnet/assets/23437060/ac576819-f24f-4fed-82cd-a05d87f9ad71) - If there is not enough space below the dropdown button to open the dropdown menu popup, then the popup opens to the top instead: ![alignment](https://github.com/ddnet/ddnet/assets/23437060/2f61ea30-996d-4def-8f5d-300b5008c666) ## Checklist - [X] Tested the change ingame - [X] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [X] Considered possible null pointers and out of bounds array indexing - [X] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
6a53d72b35
|
@ -882,6 +882,12 @@ bool CGraphicsBackend_SDL_GL::GetDriverVersion(EGraphicsDriverAgeType DriverAgeT
|
|||
return false;
|
||||
}
|
||||
|
||||
const char *CGraphicsBackend_SDL_GL::GetScreenName(int Screen) const
|
||||
{
|
||||
const char *pName = SDL_GetDisplayName(Screen);
|
||||
return pName == nullptr ? "unknown/error" : pName;
|
||||
}
|
||||
|
||||
static void DisplayToVideoMode(CVideoMode *pVMode, SDL_DisplayMode *pMode, int HiDPIScale, int RefreshRate)
|
||||
{
|
||||
pVMode->m_CanvasWidth = pMode->w * HiDPIScale;
|
||||
|
|
|
@ -253,6 +253,7 @@ public:
|
|||
const TTWGraphicsGPUList &GetGPUs() const override;
|
||||
|
||||
int GetNumScreens() const override { return m_NumScreens; }
|
||||
const char *GetScreenName(int Screen) const override;
|
||||
|
||||
void GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenID) override;
|
||||
void GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenID) override;
|
||||
|
|
|
@ -2939,6 +2939,11 @@ int CGraphics_Threaded::GetNumScreens() const
|
|||
return m_pBackend->GetNumScreens();
|
||||
}
|
||||
|
||||
const char *CGraphics_Threaded::GetScreenName(int Screen) const
|
||||
{
|
||||
return m_pBackend->GetScreenName(Screen);
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::Minimize()
|
||||
{
|
||||
m_pBackend->Minimize();
|
||||
|
|
|
@ -731,6 +731,7 @@ public:
|
|||
virtual void GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen) = 0;
|
||||
|
||||
virtual int GetNumScreens() const = 0;
|
||||
virtual const char *GetScreenName(int Screen) const = 0;
|
||||
|
||||
virtual void Minimize() = 0;
|
||||
virtual void Maximize() = 0;
|
||||
|
@ -1257,6 +1258,8 @@ public:
|
|||
void IndicesNumRequiredNotify(unsigned int RequiredIndicesCount) override;
|
||||
|
||||
int GetNumScreens() const override;
|
||||
const char *GetScreenName(int Screen) const override;
|
||||
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void WarnPngliteIncompatibleImages(bool Warn) override;
|
||||
|
|
|
@ -187,14 +187,10 @@ void CInput::CloseJoysticks()
|
|||
m_pActiveJoystick = nullptr;
|
||||
}
|
||||
|
||||
void CInput::SelectNextJoystick()
|
||||
void CInput::SetActiveJoystick(size_t Index)
|
||||
{
|
||||
const int Num = m_vJoysticks.size();
|
||||
if(Num > 1)
|
||||
{
|
||||
m_pActiveJoystick = &m_vJoysticks[(m_pActiveJoystick->GetIndex() + 1) % Num];
|
||||
str_copy(g_Config.m_InpControllerGUID, m_pActiveJoystick->GetGUID());
|
||||
}
|
||||
m_pActiveJoystick = &m_vJoysticks[Index];
|
||||
str_copy(g_Config.m_InpControllerGUID, m_pActiveJoystick->GetGUID());
|
||||
}
|
||||
|
||||
float CInput::CJoystick::GetAxisValue(int Axis)
|
||||
|
|
|
@ -122,8 +122,9 @@ public:
|
|||
bool KeyPress(int Key, bool CheckCounter) const override { return CheckCounter ? (m_aInputCount[Key] == m_InputCounter) : m_aInputCount[Key]; }
|
||||
|
||||
size_t NumJoysticks() const override { return m_vJoysticks.size(); }
|
||||
CJoystick *GetJoystick(size_t Index) override { return &m_vJoysticks[Index]; }
|
||||
CJoystick *GetActiveJoystick() override { return m_pActiveJoystick; }
|
||||
void SelectNextJoystick() override;
|
||||
void SetActiveJoystick(size_t Index) override;
|
||||
|
||||
bool MouseRelative(float *pX, float *pY) override;
|
||||
void MouseModeAbsolute() override;
|
||||
|
|
|
@ -506,6 +506,7 @@ public:
|
|||
|
||||
virtual void Swap() = 0;
|
||||
virtual int GetNumScreens() const = 0;
|
||||
virtual const char *GetScreenName(int Screen) const = 0;
|
||||
|
||||
// synchronization
|
||||
virtual void InsertSignal(class CSemaphore *pSemaphore) = 0;
|
||||
|
|
|
@ -98,8 +98,9 @@ public:
|
|||
virtual bool Absolute(float *pX, float *pY) = 0;
|
||||
};
|
||||
virtual size_t NumJoysticks() const = 0;
|
||||
virtual IJoystick *GetJoystick(size_t Index) = 0;
|
||||
virtual IJoystick *GetActiveJoystick() = 0;
|
||||
virtual void SelectNextJoystick() = 0;
|
||||
virtual void SetActiveJoystick(size_t Index) = 0;
|
||||
|
||||
// mouse
|
||||
virtual void NativeMousePos(int *pX, int *pY) const = 0;
|
||||
|
|
|
@ -662,8 +662,6 @@ private:
|
|||
void RenderSettingsAppearance(CUIRect MainView);
|
||||
ColorHSLA RenderHSLScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha = false, bool ClampedLight = false);
|
||||
|
||||
int RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, CButtonContainer *pButtonContainer, float &ScrollVal);
|
||||
|
||||
CServerProcess m_ServerProcess;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1078,20 +1078,39 @@ float CMenus::RenderSettingsControlsJoystick(CUIRect View)
|
|||
{
|
||||
// show joystick device selection if more than one available or just the joystick name if there is only one
|
||||
{
|
||||
CUIRect JoystickDropDown;
|
||||
View.HSplitTop(Spacing, nullptr, &View);
|
||||
View.HSplitTop(ButtonHeight, &Button, &View);
|
||||
char aBuf[96];
|
||||
str_format(aBuf, sizeof(aBuf), Localize("Controller %d: %s"), Input()->GetActiveJoystick()->GetIndex(), Input()->GetActiveJoystick()->GetName());
|
||||
View.HSplitTop(ButtonHeight, &JoystickDropDown, &View);
|
||||
if(NumJoysticks > 1)
|
||||
{
|
||||
static CButtonContainer s_ButtonJoystickId;
|
||||
if(DoButton_Menu(&s_ButtonJoystickId, aBuf, 0, &Button))
|
||||
Input()->SelectNextJoystick();
|
||||
GameClient()->m_Tooltips.DoToolTip(&s_ButtonJoystickId, &Button, Localize("Click to cycle through all available controllers."));
|
||||
static std::vector<std::string> s_vJoystickNames;
|
||||
static std::vector<const char *> s_vpJoystickNames;
|
||||
s_vJoystickNames.resize(NumJoysticks);
|
||||
s_vpJoystickNames.resize(NumJoysticks);
|
||||
|
||||
for(int i = 0; i < NumJoysticks; ++i)
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "%s %d: %s", Localize("Controller"), i, Input()->GetJoystick(i)->GetName());
|
||||
s_vJoystickNames[i] = aBuf;
|
||||
s_vpJoystickNames[i] = s_vJoystickNames[i].c_str();
|
||||
}
|
||||
|
||||
static CUI::SDropDownState s_JoystickDropDownState;
|
||||
static CScrollRegion s_JoystickDropDownScrollRegion;
|
||||
s_JoystickDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_JoystickDropDownScrollRegion;
|
||||
const int CurrentJoystick = Input()->GetActiveJoystick()->GetIndex();
|
||||
const int NewJoystick = UI()->DoDropDown(&JoystickDropDown, CurrentJoystick, s_vpJoystickNames.data(), s_vpJoystickNames.size(), s_JoystickDropDownState);
|
||||
if(NewJoystick != CurrentJoystick)
|
||||
{
|
||||
Input()->SetActiveJoystick(NewJoystick);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UI()->DoLabel(&Button, aBuf, 13.0f, TEXTALIGN_ML);
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "%s 0: %s", Localize("Controller"), Input()->GetJoystick(0)->GetName());
|
||||
UI()->DoLabel(&JoystickDropDown, aBuf, 13.0f, TEXTALIGN_ML);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1447,53 +1466,6 @@ void CMenus::ResetSettingsControls()
|
|||
g_Config.m_UiControllerSens = 100;
|
||||
}
|
||||
|
||||
int CMenus::RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, CButtonContainer *pButtonContainer, float &ScrollVal)
|
||||
{
|
||||
if(CurDropDownState != 0)
|
||||
{
|
||||
const float RowHeight = 24.0f;
|
||||
const float RowSpacing = 3.0f;
|
||||
CUIRect ListRect;
|
||||
pRect->HSplitTop((RowHeight + RowSpacing) * PickNum, &ListRect, pRect);
|
||||
static CListBox s_ListBox;
|
||||
s_ListBox.DoAutoSpacing(RowSpacing);
|
||||
s_ListBox.DoStart(RowHeight, PickNum, 1, 3, CurSelection, &ListRect);
|
||||
for(int i = 0; i < PickNum; ++i)
|
||||
{
|
||||
const CListboxItem Item = s_ListBox.DoNextItem(pIDs[i], CurSelection == i);
|
||||
if(!Item.m_Visible)
|
||||
continue;
|
||||
|
||||
UI()->DoLabel(&Item.m_Rect, pStr[i], 16.0f, TEXTALIGN_MC);
|
||||
}
|
||||
int NewIndex = s_ListBox.DoEnd();
|
||||
if(s_ListBox.WasItemSelected() || s_ListBox.WasItemActivated())
|
||||
{
|
||||
CurDropDownState = 0;
|
||||
return NewIndex;
|
||||
}
|
||||
else
|
||||
return CurSelection;
|
||||
}
|
||||
else
|
||||
{
|
||||
CUIRect Button;
|
||||
pRect->HSplitTop(24.0f, &Button, pRect);
|
||||
if(DoButton_MenuTab(pButtonContainer, CurSelection > -1 ? pStr[CurSelection] : "", 0, &Button, IGraphics::CORNER_ALL, NULL, NULL, NULL, NULL, 4.0f))
|
||||
CurDropDownState = 1;
|
||||
|
||||
CUIRect DropDownIcon = Button;
|
||||
DropDownIcon.HMargin(2.0f, &DropDownIcon);
|
||||
DropDownIcon.VSplitRight(5.0f, &DropDownIcon, nullptr);
|
||||
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
UI()->DoLabel(&DropDownIcon, FONT_ICON_CIRCLE_CHEVRON_DOWN, DropDownIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MR);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetCurFont(NULL);
|
||||
return CurSelection;
|
||||
}
|
||||
}
|
||||
|
||||
void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
||||
{
|
||||
CUIRect Button;
|
||||
|
@ -1581,23 +1553,19 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
}
|
||||
|
||||
// switches
|
||||
static float s_ScrollValueDrop = 0;
|
||||
CUIRect WindowModeDropDown;
|
||||
MainView.HSplitTop(20.0f, &WindowModeDropDown, &MainView);
|
||||
|
||||
const char *apWindowModes[] = {Localize("Windowed"), Localize("Windowed borderless"), Localize("Windowed fullscreen"), Localize("Desktop fullscreen"), Localize("Fullscreen")};
|
||||
static const int s_NumWindowMode = std::size(apWindowModes);
|
||||
static int s_aWindowModeIDs[s_NumWindowMode];
|
||||
const void *apWindowModeIDs[s_NumWindowMode];
|
||||
for(int i = 0; i < s_NumWindowMode; ++i)
|
||||
apWindowModeIDs[i] = &s_aWindowModeIDs[i];
|
||||
static int s_WindowModeDropDownState = 0;
|
||||
|
||||
static int s_OldSelectedBackend = -1;
|
||||
static int s_OldSelectedGPU = -1;
|
||||
const int OldWindowMode = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 4 : (g_Config.m_GfxFullscreen == 2 ? 3 : 2)) : (g_Config.m_GfxBorderless ? 1 : 0));
|
||||
|
||||
OldSelected = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 4 : (g_Config.m_GfxFullscreen == 2 ? 3 : 2)) : (g_Config.m_GfxBorderless ? 1 : 0));
|
||||
|
||||
static CButtonContainer s_WindowButton;
|
||||
const int NewWindowMode = RenderDropDown(s_WindowModeDropDownState, &MainView, OldSelected, apWindowModeIDs, apWindowModes, s_NumWindowMode, &s_WindowButton, s_ScrollValueDrop);
|
||||
if(OldSelected != NewWindowMode)
|
||||
static CUI::SDropDownState s_WindowModeDropDownState;
|
||||
static CScrollRegion s_WindowModeDropDownScrollRegion;
|
||||
s_WindowModeDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_WindowModeDropDownScrollRegion;
|
||||
const int NewWindowMode = UI()->DoDropDown(&WindowModeDropDown, OldWindowMode, apWindowModes, s_NumWindowMode, s_WindowModeDropDownState);
|
||||
if(OldWindowMode != NewWindowMode)
|
||||
{
|
||||
if(NewWindowMode == 0)
|
||||
Client()->SetWindowParams(0, false, true);
|
||||
|
@ -1611,6 +1579,37 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
Client()->SetWindowParams(1, false, true);
|
||||
}
|
||||
|
||||
if(Graphics()->GetNumScreens() > 1)
|
||||
{
|
||||
CUIRect ScreenDropDown;
|
||||
MainView.HSplitTop(2.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &ScreenDropDown, &MainView);
|
||||
|
||||
const int NumScreens = Graphics()->GetNumScreens();
|
||||
static std::vector<std::string> s_vScreenNames;
|
||||
static std::vector<const char *> s_vpScreenNames;
|
||||
s_vScreenNames.resize(NumScreens);
|
||||
s_vpScreenNames.resize(NumScreens);
|
||||
|
||||
for(int i = 0; i < NumScreens; ++i)
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), "%s %d: %s", Localize("Screen"), i, Graphics()->GetScreenName(i));
|
||||
s_vScreenNames[i] = aBuf;
|
||||
s_vpScreenNames[i] = s_vScreenNames[i].c_str();
|
||||
}
|
||||
|
||||
static CUI::SDropDownState s_ScreenDropDownState;
|
||||
static CScrollRegion s_ScreenDropDownScrollRegion;
|
||||
s_ScreenDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_ScreenDropDownScrollRegion;
|
||||
const int NewScreen = UI()->DoDropDown(&ScreenDropDown, g_Config.m_GfxScreen, s_vpScreenNames.data(), s_vpScreenNames.size(), s_ScreenDropDownState);
|
||||
if(NewScreen != g_Config.m_GfxScreen)
|
||||
{
|
||||
Client()->SwitchWindowScreen(NewScreen);
|
||||
s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
|
||||
}
|
||||
}
|
||||
|
||||
MainView.HSplitTop(2.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||
str_format(aBuf, sizeof(aBuf), "%s (%s)", Localize("V-Sync"), Localize("may cause delay"));
|
||||
if(DoButton_CheckBox(&g_Config.m_GfxVsync, aBuf, g_Config.m_GfxVsync, &Button))
|
||||
|
@ -1618,23 +1617,6 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
Client()->ToggleWindowVSync();
|
||||
}
|
||||
|
||||
if(Graphics()->GetNumScreens() > 1)
|
||||
{
|
||||
int NumScreens = Graphics()->GetNumScreens();
|
||||
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||
int Screen_MouseButton = DoButton_CheckBox_Number(&g_Config.m_GfxScreen, Localize("Screen"), g_Config.m_GfxScreen, &Button);
|
||||
if(Screen_MouseButton == 1) // inc
|
||||
{
|
||||
Client()->SwitchWindowScreen((g_Config.m_GfxScreen + 1) % NumScreens);
|
||||
s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
|
||||
}
|
||||
else if(Screen_MouseButton == 2) // dec
|
||||
{
|
||||
Client()->SwitchWindowScreen((g_Config.m_GfxScreen - 1 + NumScreens) % NumScreens);
|
||||
s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiSamplingChanged = false;
|
||||
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||
str_format(aBuf, sizeof(aBuf), "%s (%s)", Localize("FSAA samples"), Localize("may cause delay"));
|
||||
|
@ -1726,25 +1708,20 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
|
||||
if(FoundBackendCount > 1)
|
||||
{
|
||||
CUIRect Text;
|
||||
CUIRect Text, BackendDropDown;
|
||||
MainView.HSplitTop(10.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &Text, &MainView);
|
||||
MainView.HSplitTop(2.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &BackendDropDown, &MainView);
|
||||
UI()->DoLabel(&Text, Localize("Renderer"), 16.0f, TEXTALIGN_MC);
|
||||
|
||||
static float s_ScrollValueDropBackend = 0;
|
||||
static int s_BackendDropDownState = 0;
|
||||
|
||||
static std::vector<std::unique_ptr<int>> s_vBackendIDs;
|
||||
static std::vector<const void *> s_vBackendIDPtrs;
|
||||
static std::vector<std::string> s_vBackendIDNames;
|
||||
static std::vector<const char *> s_vBackendIDNamesCStr;
|
||||
static std::vector<const char *> s_vpBackendIDNamesCStr;
|
||||
static std::vector<SMenuBackendInfo> s_vBackendInfos;
|
||||
|
||||
size_t BackendCount = FoundBackendCount + 1;
|
||||
s_vBackendIDs.resize(BackendCount);
|
||||
s_vBackendIDPtrs.resize(BackendCount);
|
||||
s_vBackendIDNames.resize(BackendCount);
|
||||
s_vBackendIDNamesCStr.resize(BackendCount);
|
||||
s_vpBackendIDNamesCStr.resize(BackendCount);
|
||||
s_vBackendInfos.resize(BackendCount);
|
||||
|
||||
char aTmpBackendName[256];
|
||||
|
@ -1762,22 +1739,16 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
auto &Info = aaSupportedBackends[i][n];
|
||||
if(Info.m_Found)
|
||||
{
|
||||
if(s_vBackendIDs[CurCounter].get() == nullptr)
|
||||
s_vBackendIDs[CurCounter] = std::make_unique<int>();
|
||||
s_vBackendIDPtrs[CurCounter] = s_vBackendIDs[CurCounter].get();
|
||||
|
||||
bool IsDefault = IsInfoDefault(Info);
|
||||
str_format(aTmpBackendName, sizeof(aTmpBackendName), "%s (%d.%d.%d)%s%s", Info.m_pBackendName, Info.m_Major, Info.m_Minor, Info.m_Patch, IsDefault ? " - " : "", IsDefault ? Localize("default") : "");
|
||||
s_vBackendIDNames[CurCounter] = aTmpBackendName;
|
||||
s_vpBackendIDNamesCStr[CurCounter] = s_vBackendIDNames[CurCounter].c_str();
|
||||
if(str_comp_nocase(Info.m_pBackendName, g_Config.m_GfxBackend) == 0 && g_Config.m_GfxGLMajor == Info.m_Major && g_Config.m_GfxGLMinor == Info.m_Minor && g_Config.m_GfxGLPatch == Info.m_Patch)
|
||||
{
|
||||
bool IsDefault = IsInfoDefault(Info);
|
||||
str_format(aTmpBackendName, sizeof(aTmpBackendName), "%s (%d.%d.%d)%s%s", Info.m_pBackendName, Info.m_Major, Info.m_Minor, Info.m_Patch, IsDefault ? " - " : "", IsDefault ? Localize("default") : "");
|
||||
s_vBackendIDNames[CurCounter] = aTmpBackendName;
|
||||
s_vBackendIDNamesCStr[CurCounter] = s_vBackendIDNames[CurCounter].c_str();
|
||||
if(str_comp_nocase(Info.m_pBackendName, g_Config.m_GfxBackend) == 0 && g_Config.m_GfxGLMajor == Info.m_Major && g_Config.m_GfxGLMinor == Info.m_Minor && g_Config.m_GfxGLPatch == Info.m_Patch)
|
||||
{
|
||||
OldSelectedBackend = CurCounter;
|
||||
}
|
||||
|
||||
s_vBackendInfos[CurCounter] = Info;
|
||||
OldSelectedBackend = CurCounter;
|
||||
}
|
||||
|
||||
s_vBackendInfos[CurCounter] = Info;
|
||||
++CurCounter;
|
||||
}
|
||||
}
|
||||
|
@ -1791,13 +1762,9 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
else
|
||||
{
|
||||
// custom selected one
|
||||
if(s_vBackendIDs[CurCounter].get() == nullptr)
|
||||
s_vBackendIDs[CurCounter] = std::make_unique<int>();
|
||||
s_vBackendIDPtrs[CurCounter] = s_vBackendIDs[CurCounter].get();
|
||||
|
||||
str_format(aTmpBackendName, sizeof(aTmpBackendName), "%s (%s %d.%d.%d)", Localize("custom"), g_Config.m_GfxBackend, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch);
|
||||
s_vBackendIDNames[CurCounter] = aTmpBackendName;
|
||||
s_vBackendIDNamesCStr[CurCounter] = s_vBackendIDNames[CurCounter].c_str();
|
||||
s_vpBackendIDNamesCStr[CurCounter] = s_vBackendIDNames[CurCounter].c_str();
|
||||
OldSelectedBackend = CurCounter;
|
||||
|
||||
s_vBackendInfos[CurCounter].m_pBackendName = "custom";
|
||||
|
@ -1806,14 +1773,14 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
s_vBackendInfos[CurCounter].m_Patch = g_Config.m_GfxGLPatch;
|
||||
}
|
||||
|
||||
static int s_OldSelectedBackend = -1;
|
||||
if(s_OldSelectedBackend == -1)
|
||||
s_OldSelectedBackend = OldSelectedBackend;
|
||||
|
||||
static int s_BackendCount = 0;
|
||||
s_BackendCount = BackendCount;
|
||||
|
||||
static CButtonContainer s_BackendButton;
|
||||
const int NewBackend = RenderDropDown(s_BackendDropDownState, &MainView, OldSelectedBackend, s_vBackendIDPtrs.data(), s_vBackendIDNamesCStr.data(), s_BackendCount, &s_BackendButton, s_ScrollValueDropBackend);
|
||||
static CUI::SDropDownState s_BackendDropDownState;
|
||||
static CScrollRegion s_BackendDropDownScrollRegion;
|
||||
s_BackendDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_BackendDropDownScrollRegion;
|
||||
const int NewBackend = UI()->DoDropDown(&BackendDropDown, OldSelectedBackend, s_vpBackendIDNamesCStr.data(), BackendCount, s_BackendDropDownState);
|
||||
if(OldSelectedBackend != NewBackend)
|
||||
{
|
||||
str_copy(g_Config.m_GfxBackend, s_vBackendInfos[NewBackend].m_pBackendName);
|
||||
|
@ -1830,35 +1797,27 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
const auto &GPUList = Graphics()->GetGPUs();
|
||||
if(GPUList.m_vGPUs.size() > 1)
|
||||
{
|
||||
CUIRect Text;
|
||||
CUIRect Text, GpuDropDown;
|
||||
MainView.HSplitTop(10.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &Text, &MainView);
|
||||
UI()->DoLabel(&Text, Localize("Graphics cards"), 16.0f, TEXTALIGN_MC);
|
||||
MainView.HSplitTop(2.0f, nullptr, &MainView);
|
||||
MainView.HSplitTop(20.0f, &GpuDropDown, &MainView);
|
||||
UI()->DoLabel(&Text, Localize("Graphics card"), 16.0f, TEXTALIGN_MC);
|
||||
|
||||
static float s_ScrollValueDropGPU = 0;
|
||||
static int s_GPUDropDownState = 0;
|
||||
|
||||
static std::vector<std::unique_ptr<int>> s_vGPUIDs;
|
||||
static std::vector<const void *> s_vGPUIDPtrs;
|
||||
static std::vector<const char *> s_vGPUIDNames;
|
||||
static std::vector<const char *> s_vpGPUIDNames;
|
||||
|
||||
size_t GPUCount = GPUList.m_vGPUs.size() + 1;
|
||||
s_vGPUIDs.resize(GPUCount);
|
||||
s_vGPUIDPtrs.resize(GPUCount);
|
||||
s_vGPUIDNames.resize(GPUCount);
|
||||
s_vpGPUIDNames.resize(GPUCount);
|
||||
|
||||
char aCurDeviceName[256 + 4];
|
||||
|
||||
int OldSelectedGPU = -1;
|
||||
for(size_t i = 0; i < GPUCount; ++i)
|
||||
{
|
||||
if(s_vGPUIDs[i].get() == nullptr)
|
||||
s_vGPUIDs[i] = std::make_unique<int>();
|
||||
s_vGPUIDPtrs[i] = s_vGPUIDs[i].get();
|
||||
if(i == 0)
|
||||
{
|
||||
str_format(aCurDeviceName, sizeof(aCurDeviceName), "%s(%s)", Localize("auto"), GPUList.m_AutoGPU.m_aName);
|
||||
s_vGPUIDNames[i] = aCurDeviceName;
|
||||
str_format(aCurDeviceName, sizeof(aCurDeviceName), "%s (%s)", Localize("auto"), GPUList.m_AutoGPU.m_aName);
|
||||
s_vpGPUIDNames[i] = aCurDeviceName;
|
||||
if(str_comp("auto", g_Config.m_GfxGPUName) == 0)
|
||||
{
|
||||
OldSelectedGPU = 0;
|
||||
|
@ -1866,7 +1825,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
}
|
||||
else
|
||||
{
|
||||
s_vGPUIDNames[i] = GPUList.m_vGPUs[i - 1].m_aName;
|
||||
s_vpGPUIDNames[i] = GPUList.m_vGPUs[i - 1].m_aName;
|
||||
if(str_comp(GPUList.m_vGPUs[i - 1].m_aName, g_Config.m_GfxGPUName) == 0)
|
||||
{
|
||||
OldSelectedGPU = i;
|
||||
|
@ -1874,14 +1833,14 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
}
|
||||
}
|
||||
|
||||
static int s_GPUCount = 0;
|
||||
s_GPUCount = GPUCount;
|
||||
|
||||
static int s_OldSelectedGPU = -1;
|
||||
if(s_OldSelectedGPU == -1)
|
||||
s_OldSelectedGPU = OldSelectedGPU;
|
||||
|
||||
static CButtonContainer s_GpuButton;
|
||||
const int NewGPU = RenderDropDown(s_GPUDropDownState, &MainView, OldSelectedGPU, s_vGPUIDPtrs.data(), s_vGPUIDNames.data(), s_GPUCount, &s_GpuButton, s_ScrollValueDropGPU);
|
||||
static CUI::SDropDownState s_GpuDropDownState;
|
||||
static CScrollRegion s_GpuDropDownScrollRegion;
|
||||
s_GpuDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_GpuDropDownScrollRegion;
|
||||
const int NewGPU = UI()->DoDropDown(&GpuDropDown, OldSelectedGPU, s_vpGPUIDNames.data(), GPUCount, s_GpuDropDownState);
|
||||
if(OldSelectedGPU != NewGPU)
|
||||
{
|
||||
if(NewGPU == 0)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include "ui.h"
|
||||
#include "ui_scrollregion.h"
|
||||
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
@ -15,6 +16,8 @@
|
|||
|
||||
#include <limits>
|
||||
|
||||
using namespace FontIcons;
|
||||
|
||||
void CUIElement::Init(CUI *pUI, int RequestedRectCount)
|
||||
{
|
||||
m_pUI = pUI;
|
||||
|
@ -948,13 +951,14 @@ int CUI::DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pID, const
|
|||
return DoButtonLogic(pID, Props.m_Checked, pRect);
|
||||
}
|
||||
|
||||
int CUI::DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, int Align)
|
||||
int CUI::DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding, bool TransparentInactive)
|
||||
{
|
||||
pRect->Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f * ButtonColorMul(pButtonContainer)), IGraphics::CORNER_ALL, 3.0f);
|
||||
if(!TransparentInactive || CheckActiveItem(pButtonContainer) || HotItem() == pButtonContainer)
|
||||
pRect->Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f * ButtonColorMul(pButtonContainer)), IGraphics::CORNER_ALL, 3.0f);
|
||||
|
||||
CUIRect Label;
|
||||
pRect->VMargin(2.0f, &Label);
|
||||
DoLabel(&Label, pText, 10.0f, Align);
|
||||
pRect->Margin(Padding, &Label);
|
||||
DoLabel(&Label, pText, Size, Align);
|
||||
|
||||
return DoButtonLogic(pButtonContainer, 0, pRect);
|
||||
}
|
||||
|
@ -1273,7 +1277,7 @@ void CUI::DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect,
|
|||
*pOption = Value;
|
||||
}
|
||||
|
||||
void CUI::DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, int Corners)
|
||||
void CUI::DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, const SPopupMenuProperties &Props)
|
||||
{
|
||||
constexpr float Margin = SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN;
|
||||
if(X + Width > Screen()->w - Margin)
|
||||
|
@ -1284,11 +1288,11 @@ void CUI::DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Heig
|
|||
m_vPopupMenus.emplace_back();
|
||||
SPopupMenu *pNewMenu = &m_vPopupMenus.back();
|
||||
pNewMenu->m_pID = pID;
|
||||
pNewMenu->m_Props = Props;
|
||||
pNewMenu->m_Rect.x = X;
|
||||
pNewMenu->m_Rect.y = Y;
|
||||
pNewMenu->m_Rect.w = Width;
|
||||
pNewMenu->m_Rect.h = Height;
|
||||
pNewMenu->m_Corners = Corners;
|
||||
pNewMenu->m_pContext = pContext;
|
||||
pNewMenu->m_pfnFunc = pfnFunc;
|
||||
}
|
||||
|
@ -1322,9 +1326,9 @@ void CUI::RenderPopupMenus()
|
|||
}
|
||||
|
||||
CUIRect PopupRect = PopupMenu.m_Rect;
|
||||
PopupRect.Draw(ColorRGBA(0.5f, 0.5f, 0.5f, 0.75f), PopupMenu.m_Corners, 3.0f);
|
||||
PopupRect.Draw(PopupMenu.m_Props.m_BorderColor, PopupMenu.m_Props.m_Corners, 3.0f);
|
||||
PopupRect.Margin(SPopupMenu::POPUP_BORDER, &PopupRect);
|
||||
PopupRect.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.75f), PopupMenu.m_Corners, 3.0f);
|
||||
PopupRect.Draw(PopupMenu.m_Props.m_BackgroundColor, PopupMenu.m_Props.m_Corners, 3.0f);
|
||||
PopupRect.Margin(SPopupMenu::POPUP_MARGIN, &PopupRect);
|
||||
|
||||
EPopupMenuFunctionResult Result = PopupMenu.m_pfnFunc(PopupMenu.m_pContext, PopupRect, Active);
|
||||
|
@ -1453,14 +1457,14 @@ CUI::EPopupMenuFunctionResult CUI::PopupConfirm(void *pContext, CUIRect View, bo
|
|||
pUI->TextRender()->Text(Label.x, Label.y, SConfirmPopupContext::POPUP_FONT_SIZE, pConfirmPopup->m_aMessage, Label.w);
|
||||
|
||||
static CButtonContainer s_CancelButton;
|
||||
if(pUI->DoButton_PopupMenu(&s_CancelButton, pConfirmPopup->m_aNegativeButtonLabel, &CancelButton, TEXTALIGN_MC))
|
||||
if(pUI->DoButton_PopupMenu(&s_CancelButton, pConfirmPopup->m_aNegativeButtonLabel, &CancelButton, SConfirmPopupContext::POPUP_FONT_SIZE, TEXTALIGN_MC))
|
||||
{
|
||||
pConfirmPopup->m_Result = SConfirmPopupContext::CANCELED;
|
||||
return CUI::POPUP_CLOSE_CURRENT;
|
||||
}
|
||||
|
||||
static CButtonContainer s_ConfirmButton;
|
||||
if(pUI->DoButton_PopupMenu(&s_ConfirmButton, pConfirmPopup->m_aPositiveButtonLabel, &ConfirmButton, TEXTALIGN_MC) || (Active && pUI->ConsumeHotkey(HOTKEY_ENTER)))
|
||||
if(pUI->DoButton_PopupMenu(&s_ConfirmButton, pConfirmPopup->m_aPositiveButtonLabel, &ConfirmButton, SConfirmPopupContext::POPUP_FONT_SIZE, TEXTALIGN_MC) || (Active && pUI->ConsumeHotkey(HOTKEY_ENTER)))
|
||||
{
|
||||
pConfirmPopup->m_Result = SConfirmPopupContext::CONFIRMED;
|
||||
return CUI::POPUP_CLOSE_CURRENT;
|
||||
|
@ -1476,43 +1480,150 @@ CUI::SSelectionPopupContext::SSelectionPopupContext()
|
|||
|
||||
void CUI::SSelectionPopupContext::Reset()
|
||||
{
|
||||
m_Props = SPopupMenuProperties();
|
||||
m_aMessage[0] = '\0';
|
||||
m_pSelection = nullptr;
|
||||
m_Entries.clear();
|
||||
m_SelectionIndex = -1;
|
||||
m_vEntries.clear();
|
||||
m_vButtonContainers.clear();
|
||||
m_EntryHeight = 12.0f;
|
||||
m_EntryPadding = 0.0f;
|
||||
m_EntrySpacing = 5.0f;
|
||||
m_FontSize = 10.0f;
|
||||
m_Width = 300.0f + (SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN) * 2;
|
||||
m_AlignmentHeight = -1.0f;
|
||||
m_TransparentButtons = false;
|
||||
}
|
||||
|
||||
CUI::EPopupMenuFunctionResult CUI::PopupSelection(void *pContext, CUIRect View, bool Active)
|
||||
{
|
||||
SSelectionPopupContext *pSelectionPopup = static_cast<SSelectionPopupContext *>(pContext);
|
||||
CUI *pUI = pSelectionPopup->m_pUI;
|
||||
CScrollRegion *pScrollRegion = pSelectionPopup->m_pScrollRegion;
|
||||
|
||||
vec2 ScrollOffset(0.0f, 0.0f);
|
||||
CScrollRegionParams ScrollParams;
|
||||
ScrollParams.m_ScrollbarWidth = 10.0f;
|
||||
ScrollParams.m_ScrollbarMargin = SPopupMenu::POPUP_MARGIN;
|
||||
ScrollParams.m_ScrollbarNoMarginRight = true;
|
||||
ScrollParams.m_ScrollUnit = 3 * (pSelectionPopup->m_EntryHeight + pSelectionPopup->m_EntrySpacing);
|
||||
pScrollRegion->Begin(&View, &ScrollOffset, &ScrollParams);
|
||||
View.y += ScrollOffset.y;
|
||||
|
||||
CUIRect Slot;
|
||||
const STextBoundingBox TextBoundingBox = pUI->TextRender()->TextBoundingBox(SSelectionPopupContext::POPUP_FONT_SIZE, pSelectionPopup->m_aMessage, -1, SSelectionPopupContext::POPUP_MAX_WIDTH);
|
||||
View.HSplitTop(TextBoundingBox.m_H, &Slot, &View);
|
||||
if(pSelectionPopup->m_aMessage[0] != '\0')
|
||||
{
|
||||
const STextBoundingBox TextBoundingBox = pUI->TextRender()->TextBoundingBox(pSelectionPopup->m_FontSize, pSelectionPopup->m_aMessage, -1, pSelectionPopup->m_Width);
|
||||
View.HSplitTop(TextBoundingBox.m_H, &Slot, &View);
|
||||
if(pScrollRegion->AddRect(Slot))
|
||||
{
|
||||
pUI->TextRender()->Text(Slot.x, Slot.y, pSelectionPopup->m_FontSize, pSelectionPopup->m_aMessage, Slot.w);
|
||||
}
|
||||
}
|
||||
|
||||
pUI->TextRender()->Text(Slot.x, Slot.y, SSelectionPopupContext::POPUP_FONT_SIZE, pSelectionPopup->m_aMessage, Slot.w);
|
||||
|
||||
pSelectionPopup->m_vButtonContainers.resize(pSelectionPopup->m_Entries.size());
|
||||
pSelectionPopup->m_vButtonContainers.resize(pSelectionPopup->m_vEntries.size());
|
||||
|
||||
size_t Index = 0;
|
||||
for(const auto &Entry : pSelectionPopup->m_Entries)
|
||||
for(const auto &Entry : pSelectionPopup->m_vEntries)
|
||||
{
|
||||
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_SPACING, nullptr, &View);
|
||||
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_HEIGHT, &Slot, &View);
|
||||
if(pUI->DoButton_PopupMenu(&pSelectionPopup->m_vButtonContainers[Index], Entry.c_str(), &Slot, TEXTALIGN_ML))
|
||||
pSelectionPopup->m_pSelection = &Entry;
|
||||
if(pSelectionPopup->m_aMessage[0] != '\0' || Index != 0)
|
||||
View.HSplitTop(pSelectionPopup->m_EntrySpacing, nullptr, &View);
|
||||
View.HSplitTop(pSelectionPopup->m_EntryHeight, &Slot, &View);
|
||||
if(pScrollRegion->AddRect(Slot))
|
||||
{
|
||||
if(pUI->DoButton_PopupMenu(&pSelectionPopup->m_vButtonContainers[Index], Entry.c_str(), &Slot, pSelectionPopup->m_FontSize, TEXTALIGN_ML, pSelectionPopup->m_EntryPadding, pSelectionPopup->m_TransparentButtons))
|
||||
{
|
||||
pSelectionPopup->m_pSelection = &Entry;
|
||||
pSelectionPopup->m_SelectionIndex = Index;
|
||||
}
|
||||
}
|
||||
++Index;
|
||||
}
|
||||
|
||||
pScrollRegion->End();
|
||||
|
||||
return pSelectionPopup->m_pSelection == nullptr ? CUI::POPUP_KEEP_OPEN : CUI::POPUP_CLOSE_CURRENT;
|
||||
}
|
||||
|
||||
void CUI::ShowPopupSelection(float X, float Y, SSelectionPopupContext *pContext)
|
||||
{
|
||||
const STextBoundingBox TextBoundingBox = TextRender()->TextBoundingBox(SSelectionPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, SSelectionPopupContext::POPUP_MAX_WIDTH);
|
||||
const float PopupHeight = TextBoundingBox.m_H + pContext->m_Entries.size() * (SSelectionPopupContext::POPUP_ENTRY_HEIGHT + SSelectionPopupContext::POPUP_ENTRY_SPACING) + 10.0f;
|
||||
const STextBoundingBox TextBoundingBox = TextRender()->TextBoundingBox(pContext->m_FontSize, pContext->m_aMessage, -1, pContext->m_Width);
|
||||
const float PopupHeight = minimum((pContext->m_aMessage[0] == '\0' ? -pContext->m_EntrySpacing : TextBoundingBox.m_H) + pContext->m_vEntries.size() * (pContext->m_EntryHeight + pContext->m_EntrySpacing) + (SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN) * 2 + CScrollRegion::HEIGHT_MAGIC_FIX, Screen()->h * 0.4f);
|
||||
pContext->m_pUI = this;
|
||||
pContext->m_pSelection = nullptr;
|
||||
DoPopupMenu(pContext, X, Y, SSelectionPopupContext::POPUP_MAX_WIDTH + 10.0f, PopupHeight, pContext, PopupSelection);
|
||||
pContext->m_SelectionIndex = -1;
|
||||
pContext->m_Props.m_Corners = IGraphics::CORNER_ALL;
|
||||
if(pContext->m_AlignmentHeight >= 0.0f)
|
||||
{
|
||||
constexpr float Margin = SPopupMenu::POPUP_BORDER + SPopupMenu::POPUP_MARGIN;
|
||||
if(X + pContext->m_Width > Screen()->w - Margin)
|
||||
{
|
||||
X = maximum<float>(X - pContext->m_Width, Margin);
|
||||
}
|
||||
if(Y + pContext->m_AlignmentHeight + PopupHeight > Screen()->h - Margin)
|
||||
{
|
||||
Y -= PopupHeight;
|
||||
pContext->m_Props.m_Corners = IGraphics::CORNER_T;
|
||||
}
|
||||
else
|
||||
{
|
||||
Y += pContext->m_AlignmentHeight;
|
||||
pContext->m_Props.m_Corners = IGraphics::CORNER_B;
|
||||
}
|
||||
}
|
||||
DoPopupMenu(pContext, X, Y, pContext->m_Width, PopupHeight, pContext, PopupSelection, pContext->m_Props);
|
||||
}
|
||||
|
||||
int CUI::DoDropDown(CUIRect *pRect, int CurSelection, const char **pStrs, int Num, SDropDownState &State)
|
||||
{
|
||||
if(!State.m_Init)
|
||||
{
|
||||
State.m_UiElement.Init(this, -1);
|
||||
State.m_Init = true;
|
||||
}
|
||||
|
||||
const auto LabelFunc = [CurSelection, pStrs]() {
|
||||
return CurSelection > -1 ? pStrs[CurSelection] : "";
|
||||
};
|
||||
|
||||
SMenuButtonProperties Props;
|
||||
Props.m_HintRequiresStringCheck = true;
|
||||
Props.m_HintCanChangePositionOrSize = true;
|
||||
if(IsPopupOpen(&State.m_SelectionPopupContext))
|
||||
Props.m_Corners = IGraphics::CORNER_ALL & (~State.m_SelectionPopupContext.m_Props.m_Corners);
|
||||
if(DoButton_Menu(State.m_UiElement, &State.m_ButtonContainer, LabelFunc, pRect, Props))
|
||||
{
|
||||
State.m_SelectionPopupContext.Reset();
|
||||
State.m_SelectionPopupContext.m_Props.m_BorderColor = ColorRGBA(0.7f, 0.7f, 0.7f, 0.9f);
|
||||
State.m_SelectionPopupContext.m_Props.m_BackgroundColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f);
|
||||
for(int i = 0; i < Num; ++i)
|
||||
State.m_SelectionPopupContext.m_vEntries.emplace_back(pStrs[i]);
|
||||
State.m_SelectionPopupContext.m_EntryHeight = pRect->h;
|
||||
State.m_SelectionPopupContext.m_EntryPadding = pRect->h >= 20.0f ? 2.0f : 1.0f;
|
||||
State.m_SelectionPopupContext.m_FontSize = (State.m_SelectionPopupContext.m_EntryHeight - 2 * State.m_SelectionPopupContext.m_EntryPadding) * CUI::ms_FontmodHeight;
|
||||
State.m_SelectionPopupContext.m_Width = pRect->w;
|
||||
State.m_SelectionPopupContext.m_AlignmentHeight = pRect->h;
|
||||
State.m_SelectionPopupContext.m_TransparentButtons = true;
|
||||
ShowPopupSelection(pRect->x, pRect->y, &State.m_SelectionPopupContext);
|
||||
}
|
||||
|
||||
CUIRect DropDownIcon;
|
||||
pRect->HMargin(2.0f, &DropDownIcon);
|
||||
DropDownIcon.VSplitRight(5.0f, &DropDownIcon, nullptr);
|
||||
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
DoLabel(&DropDownIcon, FONT_ICON_CIRCLE_CHEVRON_DOWN, DropDownIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MR);
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetCurFont(nullptr);
|
||||
|
||||
if(State.m_SelectionPopupContext.m_SelectionIndex >= 0)
|
||||
{
|
||||
const int NewSelection = State.m_SelectionPopupContext.m_SelectionIndex;
|
||||
State.m_SelectionPopupContext.Reset();
|
||||
return NewSelection;
|
||||
}
|
||||
|
||||
return CurSelection;
|
||||
}
|
||||
|
||||
CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View, bool Active)
|
||||
|
|
|
@ -244,6 +244,13 @@ struct SPopupMenuId
|
|||
{
|
||||
};
|
||||
|
||||
struct SPopupMenuProperties
|
||||
{
|
||||
int m_Corners = IGraphics::CORNER_ALL;
|
||||
ColorRGBA m_BorderColor = ColorRGBA(0.5f, 0.5f, 0.5f, 0.75f);
|
||||
ColorRGBA m_BackgroundColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.75f);
|
||||
};
|
||||
|
||||
class CUI
|
||||
{
|
||||
public:
|
||||
|
@ -321,8 +328,8 @@ private:
|
|||
static constexpr float POPUP_MARGIN = 4.0f;
|
||||
|
||||
const SPopupMenuId *m_pID;
|
||||
SPopupMenuProperties m_Props;
|
||||
CUIRect m_Rect;
|
||||
int m_Corners;
|
||||
void *m_pContext;
|
||||
FPopupMenuFunction m_pfnFunc;
|
||||
};
|
||||
|
@ -491,7 +498,7 @@ public:
|
|||
|
||||
int DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pID, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props = {});
|
||||
// only used for popup menus
|
||||
int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, int Align);
|
||||
int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding = 0.0f, bool TransparentInactive = false);
|
||||
|
||||
// value selector
|
||||
int64_t DoValueSelector(const void *pID, const CUIRect *pRect, const char *pLabel, int64_t Current, int64_t Min, int64_t Max, const SValueSelectorProperties &Props = {});
|
||||
|
@ -510,7 +517,7 @@ public:
|
|||
void DoScrollbarOption(const void *pID, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale = &ms_LinearScrollbarScale, unsigned Flags = 0u, const char *pSuffix = "");
|
||||
|
||||
// popup menu
|
||||
void DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, int Corners = IGraphics::CORNER_ALL);
|
||||
void DoPopupMenu(const SPopupMenuId *pID, int X, int Y, int Width, int Height, void *pContext, FPopupMenuFunction pfnFunc, const SPopupMenuProperties &Props = {});
|
||||
void RenderPopupMenus();
|
||||
void ClosePopupMenu(const SPopupMenuId *pID, bool IncludeDescendants = false);
|
||||
void ClosePopupMenus();
|
||||
|
@ -560,16 +567,21 @@ public:
|
|||
|
||||
struct SSelectionPopupContext : public SPopupMenuId
|
||||
{
|
||||
static constexpr float POPUP_MAX_WIDTH = 300.0f;
|
||||
static constexpr float POPUP_FONT_SIZE = 10.0f;
|
||||
static constexpr float POPUP_ENTRY_HEIGHT = 12.0f;
|
||||
static constexpr float POPUP_ENTRY_SPACING = 5.0f;
|
||||
|
||||
CUI *m_pUI; // set by CUI when popup is shown
|
||||
class CScrollRegion *m_pScrollRegion;
|
||||
SPopupMenuProperties m_Props;
|
||||
char m_aMessage[256];
|
||||
std::set<std::string> m_Entries;
|
||||
std::vector<std::string> m_vEntries;
|
||||
std::vector<CButtonContainer> m_vButtonContainers;
|
||||
const std::string *m_pSelection;
|
||||
int m_SelectionIndex;
|
||||
float m_EntryHeight;
|
||||
float m_EntryPadding;
|
||||
float m_EntrySpacing;
|
||||
float m_FontSize;
|
||||
float m_Width;
|
||||
float m_AlignmentHeight;
|
||||
bool m_TransparentButtons;
|
||||
|
||||
SSelectionPopupContext();
|
||||
void Reset();
|
||||
|
@ -587,6 +599,16 @@ public:
|
|||
const char m_aValueSelectorIds[5] = {0};
|
||||
};
|
||||
void ShowPopupColorPicker(float X, float Y, SColorPickerPopupContext *pContext);
|
||||
|
||||
// dropdown menu
|
||||
struct SDropDownState
|
||||
{
|
||||
SSelectionPopupContext m_SelectionPopupContext;
|
||||
CUIElement m_UiElement;
|
||||
CButtonContainer m_ButtonContainer;
|
||||
bool m_Init = false;
|
||||
};
|
||||
int DoDropDown(CUIRect *pRect, int CurSelection, const char **pStrs, int Num, SDropDownState &State);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,11 +34,16 @@ void CScrollRegion::Begin(CUIRect *pClipRect, vec2 *pOutOffset, const CScrollReg
|
|||
const bool ContentOverflows = m_ContentH > pClipRect->h;
|
||||
const bool ForceShowScrollbar = m_Params.m_Flags & CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH;
|
||||
|
||||
const bool HasScrollBar = ContentOverflows || ForceShowScrollbar;
|
||||
CUIRect ScrollBarBg;
|
||||
bool HasScrollBar = ContentOverflows || ForceShowScrollbar;
|
||||
CUIRect *pModifyRect = HasScrollBar ? pClipRect : nullptr;
|
||||
pClipRect->VSplitRight(m_Params.m_ScrollbarWidth, pModifyRect, &ScrollBarBg);
|
||||
ScrollBarBg.Margin(m_Params.m_ScrollbarMargin, &m_RailRect);
|
||||
pClipRect->VSplitRight(m_Params.m_ScrollbarWidth, HasScrollBar ? pClipRect : nullptr, &ScrollBarBg);
|
||||
if(m_Params.m_ScrollbarNoMarginRight)
|
||||
{
|
||||
ScrollBarBg.HMargin(m_Params.m_ScrollbarMargin, &m_RailRect);
|
||||
m_RailRect.VSplitLeft(m_Params.m_ScrollbarMargin, nullptr, &m_RailRect);
|
||||
}
|
||||
else
|
||||
ScrollBarBg.Margin(m_Params.m_ScrollbarMargin, &m_RailRect);
|
||||
|
||||
// only show scrollbar if required
|
||||
if(HasScrollBar)
|
||||
|
@ -184,8 +189,8 @@ void CScrollRegion::End()
|
|||
bool CScrollRegion::AddRect(const CUIRect &Rect, bool ShouldScrollHere)
|
||||
{
|
||||
m_LastAddedRect = Rect;
|
||||
// Round up and add 1 to fix pixel clipping at the end of the scrolling area
|
||||
m_ContentH = maximum(std::ceil(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + 1.0f, m_ContentH);
|
||||
// Round up and add magic to fix pixel clipping at the end of the scrolling area
|
||||
m_ContentH = maximum(std::ceil(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + HEIGHT_MAGIC_FIX, m_ContentH);
|
||||
if(ShouldScrollHere)
|
||||
ScrollHere();
|
||||
return !IsRectClipped(Rect);
|
||||
|
|
|
@ -10,6 +10,7 @@ struct CScrollRegionParams
|
|||
bool m_Active;
|
||||
float m_ScrollbarWidth;
|
||||
float m_ScrollbarMargin;
|
||||
bool m_ScrollbarNoMarginRight;
|
||||
float m_SliderMinHeight;
|
||||
float m_ScrollUnit;
|
||||
ColorRGBA m_ClipBgColor;
|
||||
|
@ -30,6 +31,7 @@ struct CScrollRegionParams
|
|||
m_Active = true;
|
||||
m_ScrollbarWidth = 20.0f;
|
||||
m_ScrollbarMargin = 5.0f;
|
||||
m_ScrollbarNoMarginRight = false;
|
||||
m_SliderMinHeight = 25.0f;
|
||||
m_ScrollUnit = 10.0f;
|
||||
m_ClipBgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
@ -89,6 +91,10 @@ Usage:
|
|||
class CScrollRegion : private CUIElementBase
|
||||
{
|
||||
public:
|
||||
// TODO: Properly fix whatever is causing the 1-pixel discrepancy in scrolling rect height and remove this magic value.
|
||||
// Currently this must be added when calculating the required height of a UI rect for a scroll region to get a perfect fit.
|
||||
static constexpr float HEIGHT_MAGIC_FIX = 1.0f;
|
||||
|
||||
enum EScrollRelative
|
||||
{
|
||||
SCROLLRELATIVE_UP = -1,
|
||||
|
|
|
@ -5962,13 +5962,16 @@ void CEditor::RenderExtraEditorDragBar(CUIRect View, float *pSplit)
|
|||
|
||||
void CEditor::RenderMenubar(CUIRect MenuBar)
|
||||
{
|
||||
SPopupMenuProperties PopupProperties;
|
||||
PopupProperties.m_Corners = IGraphics::CORNER_R | IGraphics::CORNER_B;
|
||||
|
||||
CUIRect FileButton;
|
||||
static int s_FileButton = 0;
|
||||
MenuBar.VSplitLeft(60.0f, &FileButton, &MenuBar);
|
||||
if(DoButton_Menu(&s_FileButton, "File", 0, &FileButton, 0, nullptr))
|
||||
{
|
||||
static SPopupMenuId s_PopupMenuFileId;
|
||||
UI()->DoPopupMenu(&s_PopupMenuFileId, FileButton.x, FileButton.y + FileButton.h - 1.0f, 120.0f, 174.0f, this, PopupMenuFile, IGraphics::CORNER_R | IGraphics::CORNER_B);
|
||||
UI()->DoPopupMenu(&s_PopupMenuFileId, FileButton.x, FileButton.y + FileButton.h - 1.0f, 120.0f, 174.0f, this, PopupMenuFile, PopupProperties);
|
||||
}
|
||||
|
||||
MenuBar.VSplitLeft(5.0f, nullptr, &MenuBar);
|
||||
|
@ -5979,7 +5982,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
|
|||
if(DoButton_Menu(&s_ToolsButton, "Tools", 0, &ToolsButton, 0, nullptr))
|
||||
{
|
||||
static SPopupMenuId s_PopupMenuToolsId;
|
||||
UI()->DoPopupMenu(&s_PopupMenuToolsId, ToolsButton.x, ToolsButton.y + ToolsButton.h - 1.0f, 200.0f, 50.0f, this, PopupMenuTools, IGraphics::CORNER_R | IGraphics::CORNER_B);
|
||||
UI()->DoPopupMenu(&s_PopupMenuToolsId, ToolsButton.x, ToolsButton.y + ToolsButton.h - 1.0f, 200.0f, 50.0f, this, PopupMenuTools, PopupProperties);
|
||||
}
|
||||
|
||||
MenuBar.VSplitLeft(5.0f, nullptr, &MenuBar);
|
||||
|
@ -5990,7 +5993,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
|
|||
if(DoButton_Menu(&s_SettingsButton, "Settings", 0, &SettingsButton, 0, nullptr))
|
||||
{
|
||||
static SPopupMenuId s_PopupMenuEntitiesId;
|
||||
UI()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 64.0f, this, PopupMenuSettings, IGraphics::CORNER_R | IGraphics::CORNER_B);
|
||||
UI()->DoPopupMenu(&s_PopupMenuEntitiesId, SettingsButton.x, SettingsButton.y + SettingsButton.h - 1.0f, 200.0f, 64.0f, this, PopupMenuSettings, PopupProperties);
|
||||
}
|
||||
|
||||
CUIRect Info, Close;
|
||||
|
|
|
@ -1327,19 +1327,24 @@ CUI::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View,
|
|||
}
|
||||
|
||||
static CUI::SSelectionPopupContext s_SelectionPopupContext;
|
||||
static CScrollRegion s_SelectionPopupScrollRegion;
|
||||
s_SelectionPopupContext.m_pScrollRegion = &s_SelectionPopupScrollRegion;
|
||||
if(pEditor->DoButton_MenuItem(&s_ReaddButton, "Readd", 0, &Slot, 0, "Reloads the image from the mapres folder"))
|
||||
{
|
||||
char aFilename[IO_MAX_PATH_LENGTH];
|
||||
str_format(aFilename, sizeof(aFilename), "%s.png", pImg->m_aName);
|
||||
s_SelectionPopupContext.Reset();
|
||||
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &s_SelectionPopupContext.m_Entries);
|
||||
if(s_SelectionPopupContext.m_Entries.empty())
|
||||
std::set<std::string> EntriesSet;
|
||||
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &EntriesSet);
|
||||
for(const auto &Entry : EntriesSet)
|
||||
s_SelectionPopupContext.m_vEntries.push_back(Entry);
|
||||
if(s_SelectionPopupContext.m_vEntries.empty())
|
||||
{
|
||||
pEditor->ShowFileDialogError("Error: could not find image '%s' in the mapres folder.", aFilename);
|
||||
}
|
||||
else if(s_SelectionPopupContext.m_Entries.size() == 1)
|
||||
else if(s_SelectionPopupContext.m_vEntries.size() == 1)
|
||||
{
|
||||
s_SelectionPopupContext.m_pSelection = &*s_SelectionPopupContext.m_Entries.begin();
|
||||
s_SelectionPopupContext.m_pSelection = &s_SelectionPopupContext.m_vEntries.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1391,19 +1396,24 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSound(void *pContext, CUIRect View,
|
|||
CEditorSound *pSound = pEditor->m_Map.m_vpSounds[pEditor->m_SelectedSound];
|
||||
|
||||
static CUI::SSelectionPopupContext s_SelectionPopupContext;
|
||||
static CScrollRegion s_SelectionPopupScrollRegion;
|
||||
s_SelectionPopupContext.m_pScrollRegion = &s_SelectionPopupScrollRegion;
|
||||
if(pEditor->DoButton_MenuItem(&s_ReaddButton, "Readd", 0, &Slot, 0, "Reloads the sound from the mapres folder"))
|
||||
{
|
||||
char aFilename[IO_MAX_PATH_LENGTH];
|
||||
str_format(aFilename, sizeof(aFilename), "%s.opus", pSound->m_aName);
|
||||
s_SelectionPopupContext.Reset();
|
||||
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &s_SelectionPopupContext.m_Entries);
|
||||
if(s_SelectionPopupContext.m_Entries.empty())
|
||||
std::set<std::string> EntriesSet;
|
||||
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &EntriesSet);
|
||||
for(const auto &Entry : EntriesSet)
|
||||
s_SelectionPopupContext.m_vEntries.push_back(Entry);
|
||||
if(s_SelectionPopupContext.m_vEntries.empty())
|
||||
{
|
||||
pEditor->ShowFileDialogError("Error: could not find sound '%s' in the mapres folder.", aFilename);
|
||||
}
|
||||
else if(s_SelectionPopupContext.m_Entries.size() == 1)
|
||||
else if(s_SelectionPopupContext.m_vEntries.size() == 1)
|
||||
{
|
||||
s_SelectionPopupContext.m_pSelection = &*s_SelectionPopupContext.m_Entries.begin();
|
||||
s_SelectionPopupContext.m_pSelection = &s_SelectionPopupContext.m_vEntries.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue