diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index ff964a426..f7d393243 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -443,6 +443,7 @@ protected: void FetchAllHeaders(); void HandleDemoSeeking(float PositionToSeek, float TimeToSeek); void RenderDemoPlayer(CUIRect MainView); + void RenderDemoPlayerSliceSavePopup(CUIRect MainView); void RenderDemoList(CUIRect MainView); void PopupConfirmDeleteDemo(); @@ -646,7 +647,6 @@ public: std::chrono::nanoseconds m_PopupWarningDuration; int m_DemoPlayerState; - char m_aDemoPlayerPopupHint[256]; enum { diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index c866f22a4..8054b1d42 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -112,107 +112,8 @@ void CMenus::DemoSeekTick(IDemoPlayer::ETickOffset TickOffset) void CMenus::RenderDemoPlayer(CUIRect MainView) { const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - - const float SeekBarHeight = 15.0f; - const float ButtonbarHeight = 20.0f; - const float NameBarHeight = 20.0f; - const float Margins = 5.0f; static int64_t s_LastSpeedChange = 0; - // render popups - if(m_DemoPlayerState == DEMOPLAYER_SLICE_SAVE) - { - CUIRect Screen = *UI()->Screen(); - CUIRect Box, Part, Part2; - Box = Screen; - Box.Margin(150.0f, &Box); - - // render the box - Box.Draw(ColorRGBA(0, 0, 0, 0.5f), IGraphics::CORNER_ALL, 15.0f); - - Box.HSplitTop(20.f, 0, &Box); - Box.HSplitTop(24.f, &Part, &Box); - UI()->DoLabel(&Part, Localize("Select a name"), 24.f, TEXTALIGN_MC); - Box.HSplitTop(20.f, 0, &Box); - Box.HSplitTop(20.f, &Part, &Box); - Part.VMargin(20.f, &Part); - UI()->DoLabel(&Part, m_aDemoPlayerPopupHint, 20.f, TEXTALIGN_MC); - Box.HSplitTop(20.f, 0, &Box); - - CUIRect Label, TextBox, Ok, Abort; - - Box.HSplitBottom(20.f, &Box, 0); - Box.HSplitBottom(24.f, &Box, &Part); - Part.VMargin(80.0f, &Part); - - Part.VSplitMid(&Abort, &Ok); - - Ok.VMargin(20.0f, &Ok); - Abort.VMargin(20.0f, &Abort); - - static int s_RemoveChat = 0; - - static CButtonContainer s_ButtonAbort; - if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE)) - m_DemoPlayerState = DEMOPLAYER_NONE; - - static CButtonContainer s_ButtonOk; - if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &Ok) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER)) - { - char aDemoName[IO_MAX_PATH_LENGTH]; - DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); - str_append(aDemoName, ".demo", sizeof(aDemoName)); - - if(!str_endswith(m_DemoSliceInput.GetString(), ".demo")) - m_DemoSliceInput.Append(".demo"); - - if(str_comp(aDemoName, m_DemoSliceInput.GetString()) == 0) - str_copy(m_aDemoPlayerPopupHint, Localize("Please use a different name")); - else - { - char aPath[IO_MAX_PATH_LENGTH]; - str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString()); - - IOHANDLE DemoFile = Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_SAVE); - const char *pStr = Localize("File already exists, do you want to overwrite it?"); - if(DemoFile && str_comp(m_aDemoPlayerPopupHint, pStr) != 0) - { - io_close(DemoFile); - str_copy(m_aDemoPlayerPopupHint, pStr); - } - else - { - if(DemoFile) - io_close(DemoFile); - m_DemoPlayerState = DEMOPLAYER_NONE; - Client()->DemoSlice(aPath, CMenus::DemoFilterChat, &s_RemoveChat); - DemolistPopulate(); - DemolistOnUpdate(false); - } - } - } - - Box.HSplitTop(24.f, &Part, &Box); - Box.HSplitTop(10.f, 0, &Box); - Box.HSplitTop(24.f, &Part2, &Box); - - Part2.VSplitLeft(60.0f, 0, &Label); - if(DoButton_CheckBox(&s_RemoveChat, Localize("Remove chat"), s_RemoveChat, &Label)) - { - s_RemoveChat ^= 1; - } - - Part.VSplitLeft(60.0f, 0, &Label); - Label.VSplitLeft(120.0f, 0, &TextBox); - TextBox.VSplitLeft(20.0f, 0, &TextBox); - TextBox.VSplitRight(60.0f, &TextBox, 0); - UI()->DoLabel(&Label, Localize("New name:"), 18.0f, TEXTALIGN_ML); - if(UI()->DoEditBox(&m_DemoSliceInput, &TextBox, 12.0f)) - { - m_aDemoPlayerPopupHint[0] = '\0'; - } - } - // handle keyboard shortcuts independent of active menu float PositionToSeek = -1.0f; float TimeToSeek = 0.0f; @@ -294,7 +195,11 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } } - float TotalHeight = SeekBarHeight + ButtonbarHeight + NameBarHeight + Margins * 3; + const float SeekBarHeight = 15.0f; + const float ButtonbarHeight = 20.0f; + const float NameBarHeight = 20.0f; + const float Margins = 5.0f; + const float TotalHeight = SeekBarHeight + ButtonbarHeight + NameBarHeight + Margins * 3; // render speed info if(g_Config.m_ClDemoShowSpeed && time_get() - s_LastSpeedChange < time_freq() * 1) @@ -321,20 +226,18 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) return; } - MainView.HSplitBottom(TotalHeight, 0, &MainView); - MainView.VSplitLeft(50.0f, 0, &MainView); - MainView.VSplitLeft(600.0f, &MainView, 0); - - MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_T, 10.0f); - - MainView.Margin(5.0f, &MainView); + CUIRect DemoControls; + MainView.HSplitBottom(TotalHeight, nullptr, &DemoControls); + DemoControls.VSplitLeft(50.0f, nullptr, &DemoControls); + DemoControls.VSplitLeft(600.0f, &DemoControls, nullptr); + DemoControls.Draw(ms_ColorTabbarActive, IGraphics::CORNER_T, 10.0f); CUIRect SeekBar, ButtonBar, NameBar, SpeedBar; - - MainView.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar); - ButtonBar.HSplitTop(Margins, 0, &ButtonBar); + DemoControls.Margin(5.0f, &DemoControls); + DemoControls.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar); + ButtonBar.HSplitTop(Margins, nullptr, &ButtonBar); ButtonBar.HSplitBottom(NameBarHeight, &ButtonBar, &NameBar); - NameBar.HSplitTop(4.0f, 0, &NameBar); + NameBar.HSplitTop(4.0f, nullptr, &NameBar); // do seekbar { @@ -483,7 +386,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_PlayPauseButton, &Button, pInfo->m_Paused ? Localize("Play the current demo") : Localize("Pause the current demo")); // stop button - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_ResetButton; if(DoButton_FontIcon(&s_ResetButton, FONT_ICON_STOP, false, &Button, IGraphics::CORNER_ALL)) @@ -494,7 +397,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_ResetButton, &Button, Localize("Stop the current demo")); // one tick back - ButtonBar.VSplitLeft(Margins + 10.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins + 10.0f, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneTickBackButton; if(DoButton_FontIcon(&s_OneTickBackButton, FONT_ICON_CHEVRON_LEFT, 0, &Button, IGraphics::CORNER_ALL)) @@ -502,7 +405,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_OneTickBackButton, &Button, Localize("Go back one tick")); // one tick forward - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneTickForwardButton; if(DoButton_FontIcon(&s_OneTickForwardButton, FONT_ICON_CHEVRON_RIGHT, 0, &Button, IGraphics::CORNER_ALL)) @@ -543,7 +446,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_SliceBeginButton, &Button, Localize("Mark the beginning of a cut")); // slice end button - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_SliceEndButton; if(DoButton_FontIcon(&s_SliceEndButton, FONT_ICON_RIGHT_TO_BRACKET, 0, &Button, IGraphics::CORNER_ALL)) @@ -555,7 +458,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_SliceEndButton, &Button, Localize("Mark the end of a cut")); // slice save button - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_SliceSaveButton; if(DoButton_FontIcon(&s_SliceSaveButton, FONT_ICON_ARROW_UP_RIGHT_FROM_SQUARE, 0, &Button, IGraphics::CORNER_ALL)) @@ -565,7 +468,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) m_DemoSliceInput.Set(aDemoName); m_DemoSliceInput.Append(".demo"); UI()->SetActiveItem(&m_DemoSliceInput); - m_aDemoPlayerPopupHint[0] = '\0'; m_DemoPlayerState = DEMOPLAYER_SLICE_SAVE; } GameClient()->m_Tooltips.DoToolTip(&s_SliceSaveButton, &Button, Localize("Export cut as a separate demo")); @@ -574,7 +476,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) const int Threshold = 10; // one marker back - ButtonBar.VSplitLeft(Margins + 20.0f, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins + 20.0f, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneMarkerBackButton; if(DoButton_FontIcon(&s_OneMarkerBackButton, FONT_ICON_BACKWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) @@ -590,7 +492,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) GameClient()->m_Tooltips.DoToolTip(&s_OneMarkerBackButton, &Button, Localize("Go back one marker")); // one marker forward - ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(Margins, nullptr, &ButtonBar); ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); static CButtonContainer s_OneMarkerForwardButton; if(DoButton_FontIcon(&s_OneMarkerForwardButton, FONT_ICON_FORWARD_STEP, 0, &Button, IGraphics::CORNER_ALL)) @@ -615,7 +517,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } // toggle keyboard shortcuts button - ButtonBar.VSplitRight(Margins, &ButtonBar, 0); + ButtonBar.VSplitRight(Margins, &ButtonBar, nullptr); ButtonBar.VSplitRight(ButtonbarHeight, &ButtonBar, &Button); static CButtonContainer s_KeyboardShortcutsButton; if(DoButton_FontIcon(&s_KeyboardShortcutsButton, FONT_ICON_KEYBOARD, 0, &Button, IGraphics::CORNER_ALL, g_Config.m_ClDemoKeyboardShortcuts != 0)) @@ -631,7 +533,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName); CTextCursor Cursor; TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y + (NameBar.h - (Button.h * 0.5f)) / 2.f, Button.h * 0.5f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = MainView.w; + Cursor.m_LineWidth = DemoControls.w; TextRender()->TextEx(&Cursor, aBuf, -1); if(IncreaseDemoSpeed) @@ -646,6 +548,132 @@ void CMenus::RenderDemoPlayer(CUIRect MainView) } HandleDemoSeeking(PositionToSeek, TimeToSeek); + + // render popups + if(m_DemoPlayerState != DEMOPLAYER_NONE) + { + // prevent element under the active popup from being activated + UI()->SetHotItem(nullptr); + } + if(m_DemoPlayerState == DEMOPLAYER_SLICE_SAVE) + { + RenderDemoPlayerSliceSavePopup(MainView); + } + + UI()->RenderPopupMenus(); +} + +void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView) +{ + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); + + CUIRect Box; + MainView.Margin(150.0f, &Box); + + // background + Box.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), IGraphics::CORNER_ALL, 15.0f); + Box.Margin(24.0f, &Box); + + // title + CUIRect Title; + Box.HSplitTop(24.0f, &Title, &Box); + Box.HSplitTop(20.0f, nullptr, &Box); + UI()->DoLabel(&Title, Localize("Export demo cut"), 24.0f, TEXTALIGN_MC); + + // slice times + CUIRect SliceTimesBar, SliceInterval, SliceLength; + Box.HSplitTop(24.0f, &SliceTimesBar, &Box); + SliceTimesBar.VSplitMid(&SliceInterval, &SliceLength, 40.0f); + Box.HSplitTop(20.0f, nullptr, &Box); + const int64_t RealSliceBegin = g_Config.m_ClDemoSliceBegin == -1 ? 0 : (g_Config.m_ClDemoSliceBegin - pInfo->m_FirstTick); + const int64_t RealSliceEnd = (g_Config.m_ClDemoSliceEnd == -1 ? pInfo->m_LastTick : g_Config.m_ClDemoSliceEnd) - pInfo->m_FirstTick; + char aSliceBegin[32]; + str_time(RealSliceBegin / SERVER_TICK_SPEED * 100, TIME_HOURS, aSliceBegin, sizeof(aSliceBegin)); + char aSliceEnd[32]; + str_time(RealSliceEnd / SERVER_TICK_SPEED * 100, TIME_HOURS, aSliceEnd, sizeof(aSliceEnd)); + char aSliceLength[32]; + str_time((RealSliceEnd - RealSliceBegin) / SERVER_TICK_SPEED * 100, TIME_HOURS, aSliceLength, sizeof(aSliceLength)); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%s: %s – %s", Localize("Cut interval"), aSliceBegin, aSliceEnd); + UI()->DoLabel(&SliceInterval, aBuf, 18.0f, TEXTALIGN_ML); + str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Cut length"), aSliceLength); + UI()->DoLabel(&SliceLength, aBuf, 18.0f, TEXTALIGN_ML); + + // file name + CUIRect NameLabel, NameBox; + Box.HSplitTop(24.0f, &NameLabel, &Box); + Box.HSplitTop(20.0f, nullptr, &Box); + NameLabel.VSplitLeft(150.0f, &NameLabel, &NameBox); + NameBox.VSplitLeft(20.0f, nullptr, &NameBox); + UI()->DoLabel(&NameLabel, Localize("New name:"), 18.0f, TEXTALIGN_ML); + UI()->DoEditBox(&m_DemoSliceInput, &NameBox, 12.0f); + + // remove chat checkbox + static int s_RemoveChat = 0; + CUIRect RemoveChatCheckBox; + Box.HSplitTop(24.0f, &RemoveChatCheckBox, &Box); + Box.HSplitTop(20.0f, nullptr, &Box); + if(DoButton_CheckBox(&s_RemoveChat, Localize("Remove chat"), s_RemoveChat, &RemoveChatCheckBox)) + { + s_RemoveChat ^= 1; + } + + // buttons + CUIRect ButtonBar, AbortButton, OkButton; + Box.HSplitBottom(24.0f, &Box, &ButtonBar); + ButtonBar.VSplitMid(&AbortButton, &OkButton, 40.0f); + + static CButtonContainer s_ButtonAbort; + if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &AbortButton) || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))) + m_DemoPlayerState = DEMOPLAYER_NONE; + + static CUI::SConfirmPopupContext s_ConfirmPopupContext; + static CButtonContainer s_ButtonOk; + if(DoButton_Menu(&s_ButtonOk, Localize("Ok"), 0, &OkButton) || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))) + { + char aDemoName[IO_MAX_PATH_LENGTH]; + DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); + str_append(aDemoName, ".demo", sizeof(aDemoName)); + + if(!str_endswith(m_DemoSliceInput.GetString(), ".demo")) + m_DemoSliceInput.Append(".demo"); + + if(str_comp(aDemoName, m_DemoSliceInput.GetString()) == 0) + { + static CUI::SMessagePopupContext s_MessagePopupContext; + s_MessagePopupContext.ErrorColor(); + str_copy(s_MessagePopupContext.m_aMessage, Localize("Please use a different name")); + UI()->ShowPopupMessage(UI()->MouseX(), OkButton.y + OkButton.h + 5.0f, &s_MessagePopupContext); + } + else + { + char aPath[IO_MAX_PATH_LENGTH]; + str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString()); + if(Storage()->FileExists(aPath, IStorage::TYPE_SAVE)) + { + s_ConfirmPopupContext.Reset(); + s_ConfirmPopupContext.YesNoButtons(); + str_copy(s_ConfirmPopupContext.m_aMessage, Localize("File already exists, do you want to overwrite it?")); + UI()->ShowPopupConfirm(UI()->MouseX(), OkButton.y + OkButton.h + 5.0f, &s_ConfirmPopupContext); + } + else + s_ConfirmPopupContext.m_Result = CUI::SConfirmPopupContext::CONFIRMED; + } + } + + if(s_ConfirmPopupContext.m_Result == CUI::SConfirmPopupContext::CONFIRMED) + { + char aPath[IO_MAX_PATH_LENGTH]; + str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString()); + m_DemoPlayerState = DEMOPLAYER_NONE; + Client()->DemoSlice(aPath, CMenus::DemoFilterChat, &s_RemoveChat); + DemolistPopulate(); + DemolistOnUpdate(false); + } + if(s_ConfirmPopupContext.m_Result != CUI::SConfirmPopupContext::UNSET) + { + s_ConfirmPopupContext.Reset(); + } } int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser) diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 2b2d0c4ca..b2820d8e6 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1281,7 +1281,7 @@ CUI::EPopupMenuFunctionResult CUI::PopupMessage(void *pContext, CUIRect View, bo void CUI::ShowPopupMessage(float X, float Y, SMessagePopupContext *pContext) { - const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f)), SMessagePopupContext::POPUP_MAX_WIDTH); + const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f) + 0.5f), SMessagePopupContext::POPUP_MAX_WIDTH); float TextHeight = 0.0f; STextSizeProperties TextSizeProps{}; TextSizeProps.m_pHeight = &TextHeight; @@ -1308,7 +1308,7 @@ void CUI::SConfirmPopupContext::YesNoButtons() void CUI::ShowPopupConfirm(float X, float Y, SConfirmPopupContext *pContext) { - const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f)), SConfirmPopupContext::POPUP_MAX_WIDTH); + const float TextWidth = minimum(std::ceil(TextRender()->TextWidth(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f) + 0.5f), SConfirmPopupContext::POPUP_MAX_WIDTH); float TextHeight = 0.0f; STextSizeProperties TextSizeProps{}; TextSizeProps.m_pHeight = &TextHeight;