2020-11-01 18:45:29 +03:00
|
|
|
#include "pch.h"
|
|
|
|
#include "TTextBox.h"
|
2020-12-25 16:46:06 +03:00
|
|
|
|
|
|
|
#include "control.h"
|
2021-02-06 16:53:47 +03:00
|
|
|
#include "fullscrn.h"
|
2020-12-03 17:47:36 +03:00
|
|
|
#include "loader.h"
|
2021-11-20 19:03:22 +03:00
|
|
|
#include "pb.h"
|
2020-12-25 16:46:06 +03:00
|
|
|
#include "render.h"
|
2020-12-03 17:47:36 +03:00
|
|
|
#include "score.h"
|
|
|
|
#include "timer.h"
|
2020-11-07 18:41:14 +03:00
|
|
|
|
|
|
|
|
2022-09-06 16:57:56 +03:00
|
|
|
TTextBox::TTextBox(TPinballTable* table, int groupIndex) : TPinballComponent(table, groupIndex, true)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
|
|
|
OffsetX = 0;
|
|
|
|
OffsetY = 0;
|
|
|
|
Width = 0;
|
|
|
|
Height = 0;
|
|
|
|
BgBmp = render::background_bitmap;
|
|
|
|
Font = score::msg_fontp;
|
2022-09-06 11:58:35 +03:00
|
|
|
CurrentMessage = nullptr;
|
|
|
|
PreviousMessage = nullptr;
|
2020-12-03 17:47:36 +03:00
|
|
|
Timer = 0;
|
|
|
|
|
|
|
|
if (groupIndex > 0)
|
|
|
|
{
|
2021-02-06 16:53:47 +03:00
|
|
|
/*Full tilt: text box dimensions index is offset by resolution*/
|
2021-02-15 18:55:54 +03:00
|
|
|
int arrLength;
|
2021-02-06 16:53:47 +03:00
|
|
|
auto dimensions = loader::query_iattribute(groupIndex + fullscrn::GetResolution(), 1500, &arrLength);
|
2020-12-03 17:47:36 +03:00
|
|
|
OffsetX = dimensions[0];
|
|
|
|
OffsetY = dimensions[1];
|
|
|
|
Width = dimensions[2];
|
|
|
|
Height = dimensions[3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TTextBox::~TTextBox()
|
|
|
|
{
|
|
|
|
if (Timer)
|
|
|
|
{
|
|
|
|
if (Timer != -1)
|
|
|
|
timer::kill(Timer);
|
|
|
|
Timer = 0;
|
|
|
|
}
|
2022-09-06 11:58:35 +03:00
|
|
|
while (CurrentMessage)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2022-09-06 11:58:35 +03:00
|
|
|
TTextBoxMessage* message = CurrentMessage;
|
2020-12-03 17:47:36 +03:00
|
|
|
TTextBoxMessage* nextMessage = message->NextMessage;
|
|
|
|
delete message;
|
2022-09-06 11:58:35 +03:00
|
|
|
CurrentMessage = nextMessage;
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-06 16:57:56 +03:00
|
|
|
int TTextBox::Message(MessageCode code, float value)
|
2020-11-07 18:41:14 +03:00
|
|
|
{
|
|
|
|
return 0;
|
2020-11-29 18:50:49 +03:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:35:47 +03:00
|
|
|
void TTextBox::TimerExpired(int timerId, void* caller)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2020-12-04 18:35:47 +03:00
|
|
|
auto tb = static_cast<TTextBox*>(caller);
|
2022-09-06 11:58:35 +03:00
|
|
|
TTextBoxMessage* message = tb->CurrentMessage;
|
2020-12-03 17:47:36 +03:00
|
|
|
tb->Timer = 0;
|
|
|
|
if (message)
|
|
|
|
{
|
|
|
|
TTextBoxMessage* nextMessage = message->NextMessage;
|
|
|
|
delete message;
|
2022-09-06 11:58:35 +03:00
|
|
|
tb->CurrentMessage = nextMessage;
|
2020-12-03 17:47:36 +03:00
|
|
|
tb->Draw();
|
2022-09-07 16:01:38 +03:00
|
|
|
control::handler(MessageCode::ControlTimerExpired, tb);
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 16:38:08 +03:00
|
|
|
void TTextBox::Clear(bool lowPriorityOnly)
|
2020-11-29 18:50:49 +03:00
|
|
|
{
|
2020-12-03 17:47:36 +03:00
|
|
|
gdrv_bitmap8* bmp = BgBmp;
|
|
|
|
if (bmp)
|
|
|
|
gdrv::copy_bitmap(
|
2021-10-02 17:45:31 +03:00
|
|
|
render::vscreen,
|
2020-12-03 17:47:36 +03:00
|
|
|
Width,
|
|
|
|
Height,
|
|
|
|
OffsetX,
|
|
|
|
OffsetY,
|
|
|
|
bmp,
|
|
|
|
OffsetX,
|
|
|
|
OffsetY);
|
|
|
|
else
|
2021-11-20 19:03:22 +03:00
|
|
|
gdrv::fill_bitmap(render::vscreen, Width, Height, OffsetX, OffsetY, 0);
|
2020-12-03 17:47:36 +03:00
|
|
|
if (Timer)
|
|
|
|
{
|
|
|
|
if (Timer != -1)
|
|
|
|
timer::kill(Timer);
|
|
|
|
Timer = 0;
|
|
|
|
}
|
2023-03-17 16:38:08 +03:00
|
|
|
while (CurrentMessage && (!lowPriorityOnly || CurrentMessage->LowPriority))
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2023-03-17 16:38:08 +03:00
|
|
|
auto message = CurrentMessage;
|
|
|
|
CurrentMessage = message->NextMessage;
|
2020-12-03 17:47:36 +03:00
|
|
|
delete message;
|
|
|
|
}
|
2023-03-17 16:38:08 +03:00
|
|
|
if (CurrentMessage)
|
|
|
|
Draw();
|
2020-11-29 18:50:49 +03:00
|
|
|
}
|
|
|
|
|
2023-03-17 16:38:08 +03:00
|
|
|
void TTextBox::Display(const char* text, float time, bool lowPriority)
|
2020-11-29 18:50:49 +03:00
|
|
|
{
|
2020-12-03 17:47:36 +03:00
|
|
|
if (!text)
|
|
|
|
return;
|
|
|
|
|
2022-09-06 11:58:35 +03:00
|
|
|
if (CurrentMessage && !strcmp(text, PreviousMessage->Text))
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2022-09-06 11:58:35 +03:00
|
|
|
PreviousMessage->Refresh(time);
|
|
|
|
if (PreviousMessage == CurrentMessage)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
|
|
|
if (Timer && Timer != -1)
|
|
|
|
timer::kill(Timer);
|
2021-02-18 12:53:25 +03:00
|
|
|
if (time == -1.0f)
|
2020-12-03 17:47:36 +03:00
|
|
|
Timer = -1;
|
|
|
|
else
|
2020-12-04 18:35:47 +03:00
|
|
|
Timer = timer::set(time, this, TimerExpired);
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Timer == -1)
|
|
|
|
Clear();
|
|
|
|
|
2023-03-17 16:38:08 +03:00
|
|
|
auto message = new TTextBoxMessage(text, time, lowPriority);
|
|
|
|
if (message->Text)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2023-03-17 16:38:08 +03:00
|
|
|
if (CurrentMessage)
|
|
|
|
PreviousMessage->NextMessage = message;
|
2020-12-03 17:47:36 +03:00
|
|
|
else
|
2023-03-17 16:38:08 +03:00
|
|
|
CurrentMessage = message;
|
|
|
|
PreviousMessage = message;
|
|
|
|
if (Timer == 0)
|
|
|
|
Draw();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete message;
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 06:58:03 +02:00
|
|
|
void TTextBox::DrawImGui()
|
|
|
|
{
|
|
|
|
// Do nothing when using a font (the text will be rendered to VScreen in TTextBox::Draw)
|
2022-09-06 11:58:35 +03:00
|
|
|
if (Font || !CurrentMessage)
|
2022-08-31 06:58:03 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
char windowName[64];
|
|
|
|
SDL_Rect rect;
|
|
|
|
ImGuiWindowFlags window_flags =
|
|
|
|
ImGuiWindowFlags_NoBackground |
|
|
|
|
ImGuiWindowFlags_NoDecoration |
|
|
|
|
ImGuiWindowFlags_NoSavedSettings |
|
|
|
|
ImGuiWindowFlags_NoFocusOnAppearing |
|
|
|
|
ImGuiWindowFlags_NoInputs;
|
|
|
|
|
|
|
|
rect.x = OffsetX;
|
|
|
|
rect.y = OffsetY;
|
|
|
|
rect.w = Width;
|
|
|
|
rect.h = Height;
|
|
|
|
|
|
|
|
rect = fullscrn::GetScreenRectFromPinballRect(rect);
|
|
|
|
|
2022-09-06 11:58:35 +03:00
|
|
|
ImGui::SetNextWindowPos(ImVec2(static_cast<float>(rect.x), static_cast<float>(rect.y)));
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(static_cast<float>(rect.w), static_cast<float>(rect.h)));
|
2022-08-31 06:58:03 +02:00
|
|
|
|
|
|
|
// Use the pointer to generate a window unique name per text box
|
2022-09-06 11:58:35 +03:00
|
|
|
snprintf(windowName, sizeof(windowName), "TTextBox_%p", static_cast<void*>(this));
|
2022-08-31 06:58:03 +02:00
|
|
|
if (ImGui::Begin(windowName, nullptr, window_flags))
|
|
|
|
{
|
|
|
|
ImGui::SetWindowFontScale(fullscrn::GetScreenToPinballRatio());
|
|
|
|
|
|
|
|
// ToDo: centered text in FT
|
2022-08-31 11:11:21 +03:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, pb::TextBoxColor);
|
2022-09-06 11:58:35 +03:00
|
|
|
ImGui::TextWrapped("%s", CurrentMessage->Text);
|
2022-08-31 11:11:21 +03:00
|
|
|
ImGui::PopStyleColor();
|
2022-08-31 06:58:03 +02:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2020-12-03 17:47:36 +03:00
|
|
|
void TTextBox::Draw()
|
|
|
|
{
|
|
|
|
auto bmp = BgBmp;
|
|
|
|
if (bmp)
|
|
|
|
gdrv::copy_bitmap(
|
2021-10-02 17:45:31 +03:00
|
|
|
render::vscreen,
|
2020-12-03 17:47:36 +03:00
|
|
|
Width,
|
|
|
|
Height,
|
|
|
|
OffsetX,
|
|
|
|
OffsetY,
|
|
|
|
bmp,
|
|
|
|
OffsetX,
|
|
|
|
OffsetY);
|
|
|
|
else
|
2021-10-02 17:45:31 +03:00
|
|
|
gdrv::fill_bitmap(render::vscreen, Width, Height, OffsetX, OffsetY, 0);
|
2020-12-03 17:47:36 +03:00
|
|
|
|
2021-02-15 18:55:54 +03:00
|
|
|
bool display = false;
|
2022-09-06 11:58:35 +03:00
|
|
|
while (CurrentMessage)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2022-09-06 11:58:35 +03:00
|
|
|
if (CurrentMessage->Time == -1.0f)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2022-09-06 11:58:35 +03:00
|
|
|
if (!CurrentMessage->NextMessage)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2021-01-05 12:02:43 +03:00
|
|
|
Timer = -1;
|
2021-02-15 18:55:54 +03:00
|
|
|
display = true;
|
2020-12-11 19:03:13 +03:00
|
|
|
break;
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
2022-09-06 11:58:35 +03:00
|
|
|
else if (CurrentMessage->TimeLeft() >= -2.0f)
|
2020-12-03 17:47:36 +03:00
|
|
|
{
|
2022-09-06 11:58:35 +03:00
|
|
|
Timer = timer::set(std::max(CurrentMessage->TimeLeft(), 0.25f), this, TimerExpired);
|
2021-02-15 18:55:54 +03:00
|
|
|
display = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-06 11:58:35 +03:00
|
|
|
auto tmp = CurrentMessage;
|
|
|
|
CurrentMessage = CurrentMessage->NextMessage;
|
2021-02-15 18:55:54 +03:00
|
|
|
delete tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (display)
|
|
|
|
{
|
2021-11-20 19:03:22 +03:00
|
|
|
if (!Font)
|
2021-02-15 18:55:54 +03:00
|
|
|
{
|
2022-08-31 06:58:03 +02:00
|
|
|
// Immediate mode drawing using system font is handled by TTextBox::DrawImGui
|
2021-02-15 18:55:54 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-20 19:03:22 +03:00
|
|
|
std::vector<LayoutResult> lines{};
|
|
|
|
auto textHeight = 0;
|
2022-09-06 11:58:35 +03:00
|
|
|
for (auto text = CurrentMessage->Text; ; textHeight += Font->Height)
|
2021-02-15 18:55:54 +03:00
|
|
|
{
|
2021-11-20 19:03:22 +03:00
|
|
|
if (!text[0] || textHeight + Font->Height > Height)
|
2021-02-15 18:55:54 +03:00
|
|
|
break;
|
|
|
|
|
2021-11-20 19:03:22 +03:00
|
|
|
auto line = LayoutTextLine(text);
|
|
|
|
if (line.Start == line.End)
|
|
|
|
break;
|
|
|
|
lines.push_back(line);
|
|
|
|
text = line.End;
|
|
|
|
}
|
2021-02-15 18:55:54 +03:00
|
|
|
|
2021-11-20 19:03:22 +03:00
|
|
|
// Textboxes in FT display texts centered
|
|
|
|
auto offY = OffsetY;
|
|
|
|
if (pb::FullTiltMode)
|
|
|
|
offY += (Height - textHeight) / 2;
|
|
|
|
for (auto line : lines)
|
|
|
|
{
|
2021-02-15 18:55:54 +03:00
|
|
|
auto offX = OffsetX;
|
2021-11-20 19:03:22 +03:00
|
|
|
if (pb::FullTiltMode)
|
|
|
|
offX += (Width - line.Width) / 2;
|
|
|
|
for (auto text = line.Start; text < line.End; text++)
|
2021-02-15 18:55:54 +03:00
|
|
|
{
|
2021-11-20 19:03:22 +03:00
|
|
|
auto charBmp = Font->Chars[*text & 0x7F];
|
2021-02-15 18:55:54 +03:00
|
|
|
if (charBmp)
|
|
|
|
{
|
|
|
|
auto height = charBmp->Height;
|
|
|
|
auto width = charBmp->Width;
|
|
|
|
if (render::background_bitmap)
|
2021-11-20 19:03:22 +03:00
|
|
|
gdrv::copy_bitmap_w_transparency(render::vscreen, width, height, offX, offY, charBmp, 0,
|
2021-02-15 18:55:54 +03:00
|
|
|
0);
|
|
|
|
else
|
2021-11-20 19:03:22 +03:00
|
|
|
gdrv::copy_bitmap(render::vscreen, width, height, offX, offY, charBmp, 0, 0);
|
|
|
|
offX += charBmp->Width + Font->GapWidth;
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
|
|
|
}
|
2021-11-20 19:03:22 +03:00
|
|
|
offY += Font->Height;
|
2020-12-03 17:47:36 +03:00
|
|
|
}
|
2021-11-20 19:03:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TTextBox::LayoutResult TTextBox::LayoutTextLine(char* textStart) const
|
|
|
|
{
|
|
|
|
auto lineWidth = 0, wordWidth = 0;
|
|
|
|
char *wordBoundary = nullptr, *textEnd;
|
|
|
|
for (textEnd = textStart; ; ++textEnd)
|
|
|
|
{
|
|
|
|
auto maskedChar = textEnd[0] & 0x7F;
|
|
|
|
if (!maskedChar || maskedChar == '\n')
|
|
|
|
break;
|
|
|
|
|
|
|
|
auto charBmp = Font->Chars[maskedChar];
|
|
|
|
if (!charBmp)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto width = lineWidth + charBmp->Width + Font->GapWidth;
|
|
|
|
if (width > Width)
|
|
|
|
{
|
|
|
|
if (wordBoundary)
|
|
|
|
{
|
|
|
|
textEnd = wordBoundary;
|
|
|
|
lineWidth = wordWidth;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (maskedChar == ' ')
|
|
|
|
{
|
|
|
|
wordBoundary = textEnd;
|
|
|
|
wordWidth = width;
|
|
|
|
}
|
|
|
|
lineWidth = width;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((*textEnd & 0x7F) == ' ')
|
|
|
|
++textEnd;
|
|
|
|
if ((*textEnd & 0x7F) == '\n')
|
|
|
|
++textEnd;
|
|
|
|
return LayoutResult{textStart, textEnd, lineWidth};
|
2020-11-29 18:50:49 +03:00
|
|
|
}
|