Add main screen

This commit is contained in:
2026-05-04 22:34:19 -05:00
parent 757902063b
commit 72d44a6eda
8 changed files with 878 additions and 69 deletions

View File

@@ -62,7 +62,7 @@ set(CROSSLANG_ENABLE_BINARIES OFF)
FetchContent_MakeAvailable(TessesCrossLang)
add_executable(${PROJECT_NAME} src/main.cpp src/bigscreenwindow.cpp src/bigscreenplayer.cpp src/button.cpp src/vgrid.cpp src/hgrid.cpp src/clipper.cpp src/font.cpp src/text.cpp)
add_executable(${PROJECT_NAME} src/main.cpp src/bigscreenwindow.cpp src/bigscreenplayer.cpp src/button.cpp src/vgrid.cpp src/hgrid.cpp src/clipper.cpp src/font.cpp src/text.cpp src/textbox.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC crosslang_static)

View File

@@ -130,6 +130,8 @@ namespace Tesses::BigScreen {
int MinHeight();
};
class Button : public Widget, public std::enable_shared_from_this<Button>
{
private:
@@ -192,7 +194,6 @@ namespace Tesses::BigScreen {
SDL_Renderer* renderer;
std::shared_ptr<Widget> page;
bool running=true;
Widget* current = nullptr;
std::shared_ptr<FontCache> fc;
SDL_Color color=COLOR_BGLIGHT;
@@ -209,6 +210,7 @@ namespace Tesses::BigScreen {
void Run();
~BigScreenWindow();
std::shared_ptr<Widget> GetWidget();
void SetWidget(std::shared_ptr<Widget> widget);
void DrawRectThick(SDL_Rect rect, int sz);
std::shared_ptr<BigScreenWindow> GetRoot();
@@ -216,6 +218,8 @@ namespace Tesses::BigScreen {
void SetBackColor(SDL_Color color);
void SetBackColor(std::string color);
void SetPage(std::string name, std::shared_ptr<Widget> widget);
void Close();
};
@@ -230,16 +234,58 @@ namespace Tesses::BigScreen {
int tex_w = -1, tex_h = -1;
bool controls=false;
bool paused = false;
bool seeking=false;
public:
mpv_handle* GetMPVHandle();
VideoPlayerWidget();
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void SetPrev(std::shared_ptr<Widget> w);
std::shared_ptr<Widget> GetPrev();
void Draw(SDL_Rect& rect);
void LoadFile(std::string file);
~VideoPlayerWidget();
};
class TextBox : public Widget, public std::enable_shared_from_this<TextBox>
{
private:
std::string text;
public:
TextBox()=default;
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
std::string GetText();
void SetText(std::string text);
int MinWidth();
int MinHeight();
class TextBoxDialog : public Widget {
std::shared_ptr<TextBox> tb;
std::shared_ptr<Widget> page;
std::shared_ptr<VGrid> grid;
size_t cursor;
bool shift=false;
int x=0;
int y=-1;
void Append(char c);
void HandleKey();
public:
TextBoxDialog(std::shared_ptr<TextBox> tb, std::shared_ptr<Widget> page);
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
~TextBoxDialog()=default;
};
};
class Clipper {
SDL_Rect theRect;
SDL_Renderer* renderer;

View File

@@ -9,6 +9,7 @@ namespace Tesses::BigScreen {
//"/home/mike/Music/dir/Vids/Chicken.mp4";
mpv = mpv_create();
mpv_set_option_string(mpv, "vo", "libmpv");
if (mpv_initialize(mpv) < 0)
throw std::runtime_error("mpv init failed");
@@ -36,46 +37,108 @@ namespace Tesses::BigScreen {
}
const int CONTROLS_HEIGHT = 16;
EventResult VideoPlayerWidget::Event(SDL_Event& event, SDL_Rect& rect)
{
auto window = this->GetRoot();
if(window == nullptr) return EventResult::Ignored;
switch (event.type) {
case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_ESCAPE)
case SDL_MOUSEBUTTONUP:
this->seeking=false;
break;
case SDL_MOUSEMOTION:
{
window->SetWidget(nullptr);
this->controls = (event.motion.x > rect.x && event.motion.y >= (rect.h - CONTROLS_HEIGHT) + rect.y && event.button.x < rect.x+rect.w && event.motion.y < rect.y+rect.h);
if(seeking && event.motion.x >= rect.x && event.motion.x < rect.x+rect.w && rect.w > 0)
{
double pos = (double)(event.motion.x - rect.x) / (double)rect.w;
double length = 0;
mpv_get_property(mpv, "duration", mpv_format::MPV_FORMAT_DOUBLE, &length);
std::string seek = std::to_string(length*pos);
const char *cmd_seek[] = {"seek", seek.c_str(), "absolute",NULL};
mpv_command_async(mpv, 0, cmd_seek);
}
}
break;
case SDL_MOUSEBUTTONDOWN:
{
if(event.button.x >= rect.x && event.button.y >= rect.y && event.button.x < rect.x+rect.w && event.button.y < rect.y+rect.h)
{
window->SetCurrent(this);
if(event.button.y >= rect.y+(rect.h-CONTROLS_HEIGHT) && rect.w > 0)
{
this->seeking=true;
double pos = (double)(event.button.x - rect.x) / (double)rect.w;
double length = 0;
mpv_get_property(mpv, "duration", mpv_format::MPV_FORMAT_DOUBLE, &length);
std::string seek = std::to_string(length*pos);
const char *cmd_seek[] = {"seek", seek.c_str(), "absolute",NULL};
mpv_command_async(mpv, 0, cmd_seek);
}
else {
const char *cmd_pause[] = {"cycle", "pause", NULL};
mpv_command_async(mpv, 0, cmd_pause);
}
return EventResult::Handled;
}
}
break;
case SDL_KEYDOWN:
{
if(!window->IsCurrent(this)) return EventResult::Ignored;
if(event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN)
{
return EventResult::ParentDo;
}
if(event.key.keysym.sym == SDLK_LEFT)
{
const char *seek[] = {"seek", "-10", NULL};
mpv_command_async(mpv, 0, seek);
return EventResult::Handled;
}
if(event.key.keysym.sym == SDLK_RIGHT)
{
const char *seek[] = {"seek", "+10", NULL};
mpv_command_async(mpv, 0, seek);
return EventResult::Handled;
}
if (event.key.keysym.sym == SDLK_SPACE) {
const char *cmd_pause[] = {"cycle", "pause", NULL};
mpv_command_async(mpv, 0, cmd_pause);
mpv_get_property(mpv, "pause", mpv_format::MPV_FORMAT_FLAG, &paused);
return EventResult::Handled;
}
}
break;
}
return EventResult::Handled;
return EventResult::Ignored;
}
void VideoPlayerWidget::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(window == nullptr) return;
mpv_get_property(mpv, "pause", mpv_format::MPV_FORMAT_FLAG, &paused);
int w=rect.w, h=rect.h;
if (!tex || tex_w != w || tex_h != h) {
SDL_DestroyTexture(tex);
@@ -114,9 +177,45 @@ namespace Tesses::BigScreen {
if(tex)
SDL_RenderCopy(window->GetRenderer(), tex, NULL, &rect);
SDL_Rect controls = {
.x = rect.x,
.y = rect.y + (rect.h-CONTROLS_HEIGHT),
.w = rect.w,
.h = CONTROLS_HEIGHT
};
if(this->controls || this->paused)
{
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_HIGHLIGHT.r, COLOR_HIGHLIGHT.g, COLOR_HIGHLIGHT.b, COLOR_HIGHLIGHT.a);
SDL_RenderFillRect(window->GetRenderer(), &controls);
double length = 0;
double time_pos = 0;
mpv_get_property(mpv, "duration", mpv_format::MPV_FORMAT_DOUBLE, &length);
mpv_get_property(mpv,"time-pos",mpv_format::MPV_FORMAT_DOUBLE, &time_pos);
if(length > 0)
{
double offset = time_pos / length;
if(offset < 0.0)
offset = 0.0;
if(offset > 1.0)
offset = 1.0;
SDL_Rect r2 = {
.x=controls.x,
.y=controls.y,
.w=offset*rect.w,
.h=controls.h
};
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b, COLOR_PRIMARY.a);
SDL_RenderFillRect(window->GetRenderer(), &r2);
}
}
SDL_RenderPresent(window->GetRenderer());
}
void VideoPlayerWidget::LoadFile(std::string file)

View File

@@ -30,16 +30,20 @@ namespace Tesses::BigScreen
return 0;
}
std::shared_ptr<Widget> BigScreenWindow::GetWidget()
{
return this->page;
}
BigScreenWindow::BigScreenWindow()
{
window = SDL_CreateWindow("Tesses Big Screen", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
window = SDL_CreateWindow("Tesses Big Screen", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
this->fc = std::make_shared<FontCache>(renderer, 36);
this->fc = std::make_shared<FontCache>(renderer, 24);
running=true;
}
std::shared_ptr<FontCache> BigScreenWindow::GetFont()
{
@@ -59,7 +63,7 @@ namespace Tesses::BigScreen
void BigScreenWindow::Run()
{
while(running)
while(Tesses::Framework::TF_IsRunning())
{
SDL_Event event;
@@ -84,7 +88,7 @@ namespace Tesses::BigScreen
Tesses::Framework::TF_Sleep(1);
}
}
@@ -131,8 +135,7 @@ namespace Tesses::BigScreen
{
case SDL_QUIT:
{
running=false;
Tesses::Framework::TF_SetIsRunning(false);
return EventResult::Handled;
}
@@ -182,8 +185,10 @@ namespace Tesses::BigScreen
this->page = page;
if(page)
{
page->parent = this->weak_from_this();
SetCurrent(page.get());
}
@@ -214,4 +219,21 @@ namespace Tesses::BigScreen
return mytype;
return std::shared_ptr<BigScreenWindow>();
}
void BigScreenWindow::SetPage(std::string name, std::shared_ptr<Widget> widget)
{
auto cur = this->GetWidget();
auto vgrid = std::make_shared<VGrid>();
auto hgrid = std::make_shared<HGrid>();
hgrid->AddChild(std::make_shared<Button>("<-",[cur](std::shared_ptr<Widget> w)->void {
auto win = w->GetRoot();
if(win)
win->SetWidget(cur);
}),0);
hgrid->AddChild(std::make_shared<Text>(name),0);
vgrid->AddChild(hgrid,0);
vgrid->AddChild(widget,GRID_STRETCH);
this->SetWidget(vgrid);
}
}

View File

@@ -87,9 +87,10 @@ namespace Tesses::BigScreen
{
if(event.button.x >= rect.x && event.button.y >= rect.y && event.button.x < rect.x+rect.w && event.button.y < rect.y+rect.h)
{
if(this->cb) this->cb(this->shared_from_this());
window->SetCurrent(this);
if(this->cb) this->cb(this->shared_from_this());
return EventResult::Handled;
}
}
@@ -145,7 +146,7 @@ namespace Tesses::BigScreen
rect.w-=4;
rect.h-=4;
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_BORDER.r, COLOR_BORDER.g, COLOR_BORDER.b, COLOR_BORDER.a);
window->DrawRectThick(rect, 2);
window->DrawRectThick(rect, 3);
rect.x-=2;
rect.y-=2;
rect.w+=4;
@@ -188,7 +189,7 @@ namespace Tesses::BigScreen
this->child->parent = weak_from_this();
if(this->child)
return this->child->MinWidth() + 24;
return this->child->MinWidth() + 16;
return 128;
}
@@ -198,7 +199,7 @@ namespace Tesses::BigScreen
this->child->parent = weak_from_this();
if(this->child)
return this->child->MinHeight() + 24;
return this->child->MinHeight() + 16;
return 32;
}
}

View File

@@ -1,57 +1,260 @@
#include "bigscreen.hpp"
using namespace Tesses::BigScreen;
std::vector<std::string> pages = {
"VIDEOS",
"MUSIC",
"PICTURES",
"PLUGINS",
"SETTINGS",
"EXIT"
};
const int PAGE_VIDEO = 0;
const int PAGE_MUSIC = 1;
const int PAGE_PICTURES = 2;
const int PAGE_PLUGINS = 3;
const int PAGE_SETTINGS = 4;
const int PAGE_EXIT = 5;
class Home : public Widget, public std::enable_shared_from_this<Home> {
size_t pageIdx = 0;
public:
bool Click(SDL_Rect& rect,int x, int y)
{
auto window = this->GetRoot();
if(!window) return false;
auto font = window->GetFont();
auto charWidth = font->MaxWidth();
auto charHeight = font->MaxHeight();
auto yButtons = (rect.h-(rect.h/3))+rect.y;
auto centerBtnWidth = (charWidth * 9)+16;
auto centerBtnX = ((rect.w/2) - (centerBtnWidth / 2))+rect.x;
auto sideBtnWidth = (charWidth * 2)+16;
auto leftButtonX = centerBtnX - (sideBtnWidth+16);
auto rightButtonX = centerBtnX + centerBtnWidth + 16;
SDL_Rect rect2 = {
.x = centerBtnX,
.y=yButtons,
.w = centerBtnWidth,
.h=charHeight+16
};
if(x >= rect2.x && y >= rect2.y && x < rect2.x + rect2.w && y < rect2.y + rect2.h)
{
Select();
return true;
}
rect2 = {
.x = leftButtonX,
.y=yButtons,
.w = sideBtnWidth,
.h=charHeight+16
};
if(x >= rect2.x && y >= rect2.y && x < rect2.x + rect2.w && y < rect2.y + rect2.h)
{
if(pageIdx > 0) pageIdx--;
return true;
}
rect2 = {
.x = rightButtonX,
.y=yButtons,
.w = sideBtnWidth,
.h=charHeight+16
};
if(x >= rect2.x && y >= rect2.y && x < rect2.x + rect2.w && y < rect2.y + rect2.h)
{
if(pageIdx+1 < pages.size()) pageIdx++;
return true;
}
return false;
}
void Select()
{
auto window=GetRoot();
if(!window) return;
switch(pageIdx)
{
case PAGE_VIDEO:
{
window->SetPage("Videos", nullptr);
}
break;
case PAGE_EXIT:
Tesses::Framework::TF_SetIsRunning(false);
break;
}
}
EventResult Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
switch(event.type)
{
case SDL_MOUSEBUTTONDOWN:
{
return Click(rect,event.button.x,event.button.y) ? EventResult::Handled : EventResult::Ignored;
}
break;
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:
{
Tesses::Framework::TF_SetIsRunning(false);
return EventResult::Handled;
}
break;
case SDL_SCANCODE_LEFT:
{
if(pageIdx > 0) pageIdx--;
return EventResult::Handled;
}
break;
case SDL_SCANCODE_RIGHT:
{
if(pageIdx+1 < pages.size()) pageIdx++;
return EventResult::Handled;
}
break;
case SDL_SCANCODE_SPACE:
{
Select();
}
break;
}
}
break;
}
return EventResult::Ignored;
}
void Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
auto font = window->GetFont();
auto dt= Tesses::Framework::Date::DateTime::Now();
std::string dateTime = dt.ToString(
"%I:%M:%S %p%n%a, %b %d, %Y"
);
int myW=0, myH=0;
font->CalculateSize(dateTime,myW,myH);
font->Render(window->GetRenderer(), (rect.x + rect.w - myW) - 24, rect.y+12,dateTime,COLOR_TEXT);
font->CalculateSize("Tesses BigScreen",myW, myH);
myW = ((rect.w / 2) - (myW/2)) + rect.x;
myH = (rect.h/4) + rect.y;
font->Render(window->GetRenderer(), myW,myH, "Tesses BigScreen", COLOR_TEXT);
auto charWidth = font->MaxWidth();
auto charHeight = font->MaxHeight();
auto yButtons = (rect.h-(rect.h/3))+rect.y;
auto centerBtnWidth = (charWidth * 9)+16;
auto centerBtnX = ((rect.w/2) - (centerBtnWidth / 2))+rect.x;
auto sideBtnWidth = (charWidth * 2)+16;
auto leftButtonX = centerBtnX - (sideBtnWidth+16);
auto rightButtonX = centerBtnX + centerBtnWidth + 16;
SDL_Rect rect2 = {
.x = centerBtnX,
.y=yButtons,
.w = centerBtnWidth,
.h=charHeight+16
};
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b,COLOR_PRIMARY.a);
SDL_RenderFillRect(window->GetRenderer(), &rect2);
font->CalculateSize(pages[this->pageIdx],myW,myH);
myW = ((rect2.w/2) - (myW/2))+rect2.x;
myH = ((rect2.h/2) - (myH/2))+rect2.y;
font->Render(window->GetRenderer(),myW,myH,pages[this->pageIdx],COLOR_TEXT);
rect2 = {
.x = leftButtonX,
.y=yButtons,
.w = sideBtnWidth,
.h=charHeight+16
};
SDL_RenderFillRect(window->GetRenderer(), &rect2);
font->CalculateSize("<-",myW,myH);
myW = ((rect2.w/2) - (myW/2))+rect2.x;
myH = ((rect2.h/2) - (myH/2))+rect2.y;
font->Render(window->GetRenderer(),myW,myH,"<-",COLOR_TEXT);
rect2 = {
.x = rightButtonX,
.y=yButtons,
.w = sideBtnWidth,
.h=charHeight+16
};
SDL_RenderFillRect(window->GetRenderer(), &rect2);
font->CalculateSize("->",myW,myH);
myW = ((rect2.w/2) - (myW/2))+rect2.x;
myH = ((rect2.h/2) - (myH/2))+rect2.y;
font->Render(window->GetRenderer(),myW,myH,"->",COLOR_TEXT);
}
};
int main(int argc, char** argv)
{
Tesses::Framework::TF_Init();
TTF_Init();
SDL_Init(SDL_INIT_EVERYTHING);
using namespace Tesses::BigScreen;
auto window = std::make_shared<BigScreenWindow>();
/*window->SetWidget(std::make_shared<Button>("My Button",[](std::shared_ptr<Tesses::BigScreen::Widget> w)->void{
std::cout << "Hi" << std::endl;
}));*/
std::shared_ptr<VGrid> vgrid = std::make_shared<VGrid>();
vgrid->AddChild(std::make_shared<Text>("Label"),0);
auto win = std::make_shared<BigScreenWindow>();
win->SetWidget(std::make_shared<Home>());
win->Run();
vgrid->AddChild(std::make_shared<Button>("My Button",[](std::shared_ptr<Widget> w)->void{
std::cout <<"My Button" <<std::endl;
}), 0);
vgrid->AddChild(std::make_shared<Text>("Label 2"),0);
vgrid->AddChild(std::make_shared<Button>("My Button 2",[](std::shared_ptr<Widget> w)->void{
std::cout <<"My Button 2" <<std::endl;
}), 0);
auto hgrid = std::make_shared<HGrid>();
vgrid->AddChild(nullptr, GRID_STRETCH);
hgrid->AddChild(nullptr,GRID_STRETCH);
hgrid->AddChild(std::make_shared<Button>("<-", [](std::shared_ptr<Widget> widget)->void {
std::cout << "<-" << std::endl;
}),0);
hgrid->AddChild(nullptr,16);
hgrid->AddChild(std::make_shared<Button>("OK", [](std::shared_ptr<Widget> widget)->void {
std::cout << "OK" << std::endl;
}),0);
hgrid->AddChild(nullptr,16);
hgrid->AddChild(std::make_shared<Button>("Cancel", [](std::shared_ptr<Widget> widget)->void {
std::cout << "Cancel" << std::endl;
}),0);
hgrid->AddChild(nullptr, 32);
vgrid->AddChild(hgrid, 0);
vgrid->AddChild(nullptr, 32);
window->SetWidget(vgrid);
window->Run();
return 0;
}

438
src/textbox.cpp Normal file
View File

@@ -0,0 +1,438 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen
{
EventResult TextBox::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
switch(event.type)
{
case SDL_MOUSEBUTTONDOWN:
{
if(event.button.x >= rect.x && event.button.y >= rect.y && event.button.x < rect.x+rect.w && event.button.y < rect.y+rect.h)
{
std::shared_ptr<TextBoxDialog> dlg = std::make_shared<TextBoxDialog>(shared_from_this(),window->GetWidget());
window->SetWidget(dlg);
return EventResult::Handled;
}
}
break;
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_Scancode::SDL_SCANCODE_SPACE:
{
if(window->IsCurrent(this))
{
std::shared_ptr<TextBoxDialog> dlg = std::make_shared<TextBoxDialog>(shared_from_this(),window->GetWidget());
window->SetWidget(dlg);
return EventResult::Handled;
}
}
break;
case SDL_Scancode::SDL_SCANCODE_UP:
case SDL_Scancode::SDL_SCANCODE_DOWN:
case SDL_Scancode::SDL_SCANCODE_LEFT:
case SDL_Scancode::SDL_SCANCODE_RIGHT:
{
if(window->IsCurrent(this))
return EventResult::ParentDo;
}
break;
}
}
break;
}
return EventResult::Ignored;
}
std::string TextBox::GetText()
{
return this->text;
}
void TextBox::SetText(std::string text)
{
this->text = text;
}
void TextBox::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
Clipper clipper(window->GetRenderer(),rect);
if(clipper.Clip(rect))
{
auto font = window->GetFont();
auto height = (rect.h/2) - (font->MaxHeight()/2);
font->Render(window->GetRenderer(),rect.x+10,rect.y+height,text,COLOR_TEXT);
}
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b, 255);
window->DrawRectThick(rect,6);
if(window->IsCurrent(this))
{
rect.x+=2;
rect.y+=2;
rect.w-=4;
rect.h-=4;
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_BORDER.r, COLOR_BORDER.g, COLOR_BORDER.b, COLOR_BORDER.a);
window->DrawRectThick(rect, 2);
rect.x-=2;
rect.y-=2;
rect.w+=4;
rect.h+=4;
}
}
int TextBox::MinWidth()
{
auto window = this->GetRoot();
if(!window) return 0;
int x=0;
int y=0;
window->GetFont()->CalculateSize(text,x,y);
return x+16;
}
int TextBox::MinHeight()
{
auto window = this->GetRoot();
if(!window) return 0;
int x=0;
int y=0;
window->GetFont()->CalculateSize(text,x,y);
return y+16;
}
const int KEYBOARD_WIDTH = 14;
const int KEYBOARD_HEIGHT = 4;
const char* lower[KEYBOARD_HEIGHT][KEYBOARD_WIDTH]= {
{
"`","1","2","3","4","5","6","7","8","9","0","-","=","BS"
},
{
"q","w","e","r","t","y","u","i","o","p","[","]","\\"," ",
},
{
"a","s","d","f","g","h","i","j","k","l",";","'","<-","->"
},
{
"SH","z","x","c","v","b","n","m",",",".","/","SH"," ","OK",
}
};
const char* upper[KEYBOARD_HEIGHT][KEYBOARD_WIDTH]= {
{
"~","!","@","#","$","%","^","&","*","(",")","_","+","BS"
},
{
"Q","W","E","R","T","Y","U","I","O","P","{","}","|"," ",
},
{
"A","S","D","F","G","H","I","J","K","L",":","\"","<-","->"
},
{
"SH","Z","X","C","V","B","N","M","<",">","?","SH"," ","OK",
}
};
void TextBox::TextBoxDialog::Append(char c)
{
if(c == '\b')
{
auto& pos = this->cursor;
if(pos >= this->tb->text.size())
pos = this->tb->text.size();
if(pos > 0)
{
pos--;
this->tb->text.erase(pos,1);
}
}
else {
auto& pos = this->cursor;
if(pos >= this->tb->text.size())
pos = this->tb->text.size();
this->tb->text.insert(pos, {c});
pos++;
}
}
TextBox::TextBoxDialog::TextBoxDialog(std::shared_ptr<TextBox> tb, std::shared_ptr<Widget> page): tb(tb), page(page)
{
this->cursor = tb->text.size();
}
void TextBox::TextBoxDialog::HandleKey()
{
if(y == -1) return;
if((x == 0 || x == 11) && y == 3) {
shift=!shift;
}
else if(x==13 && y ==3) {
auto window = this->GetRoot();
if(!window) return;
window->SetWidget(this->page);
if(this->tb)
window->SetCurrent(this->tb.get());
}
else if(x == 12 && y == 2)
{
this->cursor--;
if(this->cursor >= this->tb->text.size())
this->cursor=0;
}
else if(x == 13 && y == 2)
{
this->cursor++;
if(this->cursor >= this->tb->text.size())
this->cursor=this->tb->text.size();
}
else if(x == 13 && y == 0)
{
Append('\b');
}
else {
if(x >= 0 && y >= 0 && x < KEYBOARD_WIDTH && y < KEYBOARD_HEIGHT)
Append(((shift ? upper : lower)[y][x])[0]);
shift=false;
}
}
EventResult TextBox::TextBoxDialog::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
switch(event.type)
{
case SDL_MOUSEBUTTONDOWN:
{
int btnTextSzW=0;
int btnTextSzH=0;
window->GetFont()->CalculateSize("OO",btnTextSzW,btnTextSzH);
int btnSzW = btnTextSzW+16;
int btnSzH = btnTextSzW+16;
int keyboardHeight = (KEYBOARD_HEIGHT * btnSzH) + 16;
int keyboardWidth = (KEYBOARD_WIDTH * btnSzW) + 16;
int keyboardX = ((rect.w/2) - (keyboardWidth/2))+rect.x;
int keyboardY = rect.y+(rect.h-keyboardHeight);
for(int y = 0; y < KEYBOARD_HEIGHT; y++)
{
for(int x = 0; x < KEYBOARD_WIDTH; x++)
{
auto keyX = keyboardX + (x*btnSzW);
auto keyY = keyboardY + (y*btnSzH);
if(event.button.x < keyX) continue;
if(event.button.y < keyY) continue;
if(event.button.x >= (keyX + btnSzW)) continue;
if(event.button.y >= (keyY + btnSzH)) continue;
this->x = x;
this->y = y;
HandleKey();
}
}
}
break;
case SDL_TEXTINPUT:
{
if(event.text.text[0] != ' ') {
Append(event.text.text[0]);
}
}
break;
case SDL_KEYDOWN:
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_SPACE:
{
if(this->y == -1)
{
Append(' ');
}
else
{
HandleKey();
}
}
break;
case SDL_SCANCODE_UP:
{
this->y--;
if(this->y < -1) this->y=-1;
}
break;
case SDL_SCANCODE_DOWN:
{
this->y++;
if(this->y >= KEYBOARD_HEIGHT) this->y=KEYBOARD_HEIGHT-1;
}
break;
case SDL_SCANCODE_LEFT:
{
if(this->y == -1)
{
this->cursor--;
if(this->cursor >= this->tb->text.size())
this->cursor=0;
}
else {
this->x--;
if(this->x < 0) this->x=0;
}
}
break;
case SDL_SCANCODE_RIGHT:
{
if(this->y == -1)
{
this->cursor++;
if(this->cursor >= this->tb->text.size())
this->cursor=this->tb->text.size();
}
else {
this->x++;
if(this->x >= KEYBOARD_WIDTH) this->x=KEYBOARD_WIDTH-1;
}
}
break;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_RETURN2:
case SDL_SCANCODE_ESCAPE:
{
window->SetWidget(this->page);
if(this->tb)
window->SetCurrent(this->tb.get());
return EventResult::Handled;
}
break;
case SDL_SCANCODE_BACKSPACE:
{
Append('\b');
}
break;
}
break;
}
return EventResult::Ignored;
}
void TextBox::TextBoxDialog::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
if(this->page)
this->page->Draw(rect);
int btnTextSzW=0;
int btnTextSzH=0;
auto font = window->GetFont();
font->CalculateSize("OO",btnTextSzW,btnTextSzH);
auto textWidth = font->MaxWidth();
int btnSzW = btnTextSzW+16;
int btnSzH = btnTextSzW+16;
int keyboardHeight = (KEYBOARD_HEIGHT * btnSzH) + 16;
int keyboardWidth = (KEYBOARD_WIDTH * btnSzW) + 16;
int keyboardX = ((rect.w/2) - (keyboardWidth/2))+8+rect.x;
int keyboardY = rect.y+8+(rect.h-keyboardHeight);
SDL_SetRenderDrawColor(window->GetRenderer(), 0,0,0,200);
SDL_RenderFillRect(window->GetRenderer(), &rect);
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_PRIMARY.r,COLOR_PRIMARY.g,COLOR_PRIMARY.b,COLOR_PRIMARY.a);
int height = font->MaxHeight()+16;
SDL_Rect r2 = {
.x = rect.x,
.y=((rect.h-keyboardHeight)/2) - (height/2),
.w = rect.w,
.h = height
};
SDL_RenderFillRect(window->GetRenderer(), &r2);
auto maxChars = (rect.w-16) / textWidth;
if(tb->text.size() <= maxChars)
{
font->Render(window->GetRenderer(), rect.x+8, ((rect.h-keyboardHeight)/2) - (height/2) + 8, tb->text, COLOR_TEXT);
}
else {
auto page = this->cursor / maxChars;
font->Render(window->GetRenderer(), rect.x+8,((rect.h-keyboardHeight)/2) - (height/2) + 8, tb->text.substr(page*maxChars,maxChars), COLOR_TEXT);
}
int cur = (int)(this->cursor % maxChars);
r2 = {.x=rect.x+8+(textWidth*cur),.y=((rect.h-keyboardHeight)/2) - (height/2)+ height-4,.w=textWidth,.h=4};
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_HIGHLIGHT.r,COLOR_HIGHLIGHT.g,COLOR_HIGHLIGHT.b,COLOR_HIGHLIGHT.a);
SDL_RenderFillRect(window->GetRenderer(), &r2);
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_PRIMARY.r,COLOR_PRIMARY.g,COLOR_PRIMARY.b,COLOR_PRIMARY.a);
r2 = {
.x = rect.x,
.y=(rect.h-keyboardHeight)+rect.y,
.w = rect.w,
.h = keyboardHeight
};
SDL_RenderFillRect(window->GetRenderer(), &r2);
for(int y=0;y<KEYBOARD_HEIGHT; y++)
{
for(int x = 0; x < KEYBOARD_WIDTH; x++)
{
int textX = (x*btnSzW) + keyboardX;
int textY = (y*btnSzH) + keyboardY;
font->Render(window->GetRenderer(), textX, textY, (shift ? upper : lower)[y][x], COLOR_TEXT);
if(this->x == x && this->y == y)
{
SDL_Rect r = {.x=textX-8,.y=textY-8,.w = btnSzW, .h=btnSzH};
SDL_SetRenderDrawColor(window->GetRenderer(), COLOR_HIGHLIGHT.r, COLOR_HIGHLIGHT.g,COLOR_HIGHLIGHT.b,COLOR_HIGHLIGHT.a);
window->DrawRectThick(r,6);
}
}
}
}
}