First commit

This commit is contained in:
2026-05-03 18:32:46 -05:00
commit 757902063b
14 changed files with 2095 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build

82
CMakeLists.txt Normal file
View File

@@ -0,0 +1,82 @@
cmake_minimum_required(VERSION 3.16)
project(bigscreen)
set(CMAKE_CXX_STANDARD 17)
include(FetchContent)
find_package(PkgConfig REQUIRED)
pkg_check_modules(MPV REQUIRED mpv)
# 1. Fetch SDL2
FetchContent_Declare(
SDL2
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG release-2.32.10 # Use latest stable tag
GIT_SHALLOW TRUE
)
set(BUILD_SHARED_LIBS OFF)
set(SDLTTF_VENDORED ON)
set(SDLIMAGE_VENDORED ON)
FetchContent_MakeAvailable(SDL2)
# 2. Configure SDL2_image
FetchContent_Declare(
SDL2_image
GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git
GIT_TAG release-2.8.12 # Use latest stable tag
GIT_SHALLOW TRUE
)
# Critical: Disable installation to avoid header path conflicts
set(SDL2IMAGE_INSTALL OFF)
# Critical: Set BUILD_SHARED_LIBS to FALSE if you want static linking, or TRUE for shared
FetchContent_MakeAvailable(SDL2_image)
# 3. Configure SDL2_ttf
FetchContent_Declare(
SDL2_ttf
GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git
GIT_TAG release-2.24.0 # Use latest stable tag
GIT_SHALLOW TRUE
)
# Critical: Disable installation to avoid header path conflicts
set(SDL2TTF_INSTALL OFF)
# Note: SDL2_ttf may also respect BUILD_SHARED_LIBS
FetchContent_MakeAvailable(SDL2_ttf)
FetchContent_Declare(
TessesCrossLang
GIT_REPOSITORY https://git.tesses.org/tesses50/crosslang
GIT_SHALLOW TRUE
)
set(TESSESFRAMEWORK_ENABLE_STATIC ON)
set(TESSESFRAMEWORK_ENABLE_SHARED OFF)
set(TESSESFRAMEWORK_ENABLE_APPS OFF)
set(TESSESFRAMEWORK_ENABLE_EXAMPLES OFF)
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)
target_link_libraries(${PROJECT_NAME} PUBLIC crosslang_static)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_link_libraries(${PROJECT_NAME} PUBLIC
SDL2::SDL2main
SDL2::SDL2-static
SDL2_image::SDL2_image-static
SDL2_ttf::SDL2_ttf-static
)
target_include_directories(${PROJECT_NAME} PRIVATE ${MPV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${MPV_LIBRARIES})
# Ensure linker flags are added (important for some systems)
target_link_options(${PROJECT_NAME} PRIVATE ${MPV_LDFLAGS})

254
include/bigscreen.hpp Normal file
View File

@@ -0,0 +1,254 @@
#pragma once
#include <SDL2/SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <memory>
#include <mpv/client.h>
#include <mpv/render_gl.h>
#include <TessesFramework/TessesFramework.hpp>
namespace Tesses::BigScreen {
constexpr int GRID_STRETCH_SZ(int ammount)
{
return -ammount;
}
constexpr int GRID_STRETCH = GRID_STRETCH_SZ(1);
/*
--bg-dark: hsl(136 100% 0%);
--bg: hsl(133 100% 2%);
--bg-light: hsl(134 100% 4%);
--text: hsl(127 100% 90%);
--text-muted: hsl(128 34% 64%);
--highlight: hsl(144 100% 15%);
--border: hsl(133 100% 9%);
--border-muted: hsl(131 100% 4%);
--primary: hsl(134 57% 56%);
--secondary: hsl(301 74% 72%);
--danger: hsl(7 94% 66%);
--warning: hsl(53 100% 24%);
--success: hsl(163 100% 26%);
--info: hsl(217 100% 70%);
*/
constexpr SDL_Color COLOR_BGDARK = {.r = 0,.g=0,.b=0,.a=255};
constexpr SDL_Color COLOR_BG = {.r = 0,.g=10,.b=2,.a=255};
constexpr SDL_Color COLOR_BGLIGHT = {.r = 0,.g=20,.b=5,.a=255};
constexpr SDL_Color COLOR_TEXT = {.r = 204,.g=255,.b=210,.a=255};
constexpr SDL_Color COLOR_TEXTMUTED = {.r = 132,.g=194,.b=140,.a=255};
/* #004D1F*/
constexpr SDL_Color COLOR_HIGHLIGHT = {.r = 0,.g=77,.b=31,.a=255};
/* #002E0A */
constexpr SDL_Color COLOR_BORDER = {.r = 0,.g=46,.b=10,.a=255};
constexpr SDL_Color COLOR_BORDERMUTED = {.r = 0,.g=20,.b=4,.a=255};
constexpr SDL_Color COLOR_PRIMARY = {.r = 79,.g=207,.b=109,.a=255};
class BigScreenWindow;
enum class EventResult {
Ignored,
Handled,
ParentDo
};
enum class LastDirection {
North,
South,
East,
West
};
class Widget {
public:
std::weak_ptr<Widget> parent;
virtual EventResult Event(SDL_Event& event, SDL_Rect& rect) = 0;
virtual void Draw(SDL_Rect& rect)=0;
virtual std::shared_ptr<BigScreenWindow> GetRoot();
virtual ~Widget() = default;
virtual bool CanFocus();
virtual int MinHeight();
virtual int MinWidth();
};
class VGrid : public Widget, public std::enable_shared_from_this<VGrid>
{
std::vector<std::pair<std::shared_ptr<Widget>, int>> widgets;
public:
void AddChild(std::shared_ptr<Widget> widget, int height);
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
~VGrid() = default;
int MinWidth();
int MinHeight();
};
class HGrid : public Widget, public std::enable_shared_from_this<HGrid>
{
std::vector<std::pair<std::shared_ptr<Widget>, int>> widgets;
public:
void AddChild(std::shared_ptr<Widget> widget, int width);
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
~HGrid() = default;
int MinWidth();
int MinHeight();
};
class Text : public Widget
{
private:
std::string text;
SDL_Color color;
public:
Text(std::string text);
Text(std::string text, std::string color);
Text(std::string text, SDL_Color color);
bool CanFocus();
SDL_Color GetColor();
void SetColor(SDL_Color color);
void SetColor(std::string color);
std::string GetText();
void SetText(std::string text);
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
int MinWidth();
int MinHeight();
};
class Button : public Widget, public std::enable_shared_from_this<Button>
{
private:
std::shared_ptr<Widget> child;
std::function<void(std::shared_ptr<Widget> w)> cb;
SDL_Color color;
public:
Button(std::string text, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::string text, std::string textColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::string text, std::string textColor, std::string backColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::string text, SDL_Color textColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::string text, SDL_Color textColor, SDL_Color backColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::shared_ptr<Widget> widget, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::shared_ptr<Widget> widget, std::string backColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
Button(std::shared_ptr<Widget> widget, SDL_Color backColor, std::function<void(std::shared_ptr<Widget> parent)> cb);
void SetBackColor(SDL_Color color);
void SetBackColor(std::string color);
SDL_Color GetBackColor();
void SetWidget(std::shared_ptr<Widget> widget);
std::shared_ptr<Widget> GetWidget();
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
~Button();
int MinWidth();
int MinHeight();
};
class FontCache
{
std::array<SDL_Texture*,96> font_chrs;
int mw,mh,ps;
void Load(SDL_Renderer* renderer,TTF_Font* font);
public:
FontCache(SDL_Renderer* renderer,TTF_Font* font);
FontCache(SDL_Renderer* renderer,std::string font,int sz);
FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz);
FontCache(SDL_Renderer* renderer,const std::vector<uint8_t>& v,int sz);
FontCache(SDL_Renderer* renderer,int sz);
SDL_Texture* operator[](char c);
SDL_Texture* GetCharOfColor(char c, const SDL_Color& color);
int MaxWidth();
int MaxHeight();
int PointSize();
void CalculateSize(std::string text, int& x,int& y);
void Render(SDL_Renderer* renderer,int x,int y, std::string text, const SDL_Color& color);
~FontCache();
};
bool TryParseSDLColor(std::string str, SDL_Color& col);
class BigScreenWindow : public Widget, public std::enable_shared_from_this<BigScreenWindow> {
SDL_Window* window;
SDL_Renderer* renderer;
std::shared_ptr<Widget> page;
bool running=true;
Widget* current = nullptr;
std::shared_ptr<FontCache> fc;
SDL_Color color=COLOR_BGLIGHT;
public:
std::shared_ptr<FontCache> GetFont();
LastDirection dir=LastDirection::South;
void SetCurrent(Widget* widget);
bool IsCurrent(Widget* widget);
BigScreenWindow();
SDL_Window* GetWindow();
SDL_Renderer* GetRenderer();
EventResult Event(SDL_Event& event, SDL_Rect& rect);
void Draw(SDL_Rect& rect);
void Run();
~BigScreenWindow();
void SetWidget(std::shared_ptr<Widget> widget);
void DrawRectThick(SDL_Rect rect, int sz);
std::shared_ptr<BigScreenWindow> GetRoot();
void SetBackColor(SDL_Color color);
void SetBackColor(std::string color);
};
class VideoPlayerWidget : public Widget
{
private:
mpv_handle *mpv;
mpv_render_context *mpv_rd;
SDL_Texture* tex=NULL;
int tex_w = -1, tex_h = -1;
public:
mpv_handle* GetMPVHandle();
VideoPlayerWidget();
EventResult Event(SDL_Event& event,SDL_Rect& rect);
void Draw(SDL_Rect& rect);
void LoadFile(std::string file);
~VideoPlayerWidget();
};
class Clipper {
SDL_Rect theRect;
SDL_Renderer* renderer;
bool isClipped;
public:
Clipper(SDL_Renderer* renderer, SDL_Rect& myRect);
bool Clip(SDL_Rect rect);
~Clipper();
static void ClipRect(SDL_Rect& child, SDL_Rect& parent);
};
}

12
include/font.h Normal file

File diff suppressed because one or more lines are too long

143
src/bigscreenplayer.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen {
VideoPlayerWidget::VideoPlayerWidget()
{
//this->mouseY=0;
//"/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");
int one = 1;
mpv_render_param params[] = {
{MPV_RENDER_PARAM_API_TYPE, (void*)MPV_RENDER_API_TYPE_SW},
// Tell libmpv that you will call mpv_render_context_update() on render
// context update callbacks, and that you will _not_ block on the core
// ever (see <libmpv/render.h> "Threading" section for what libmpv
// functions you can call at all when this is active).
// In particular, this means you must call e.g. mpv_command_async()
// instead of mpv_command().
// If you want to use synchronous calls, either make them on a separate
// thread, or remove the option below (this will disable features like
// DR and is not recommended anyway).
{MPV_RENDER_PARAM_ADVANCED_CONTROL, &one},
{(mpv_render_param_type)0}
};
if (mpv_render_context_create(&mpv_rd, mpv, params) < 0)
throw std::runtime_error("failed to initialize mpv render context");
}
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)
{
window->SetWidget(nullptr);
}
if(event.key.keysym.sym == SDLK_LEFT)
{
const char *seek[] = {"seek", "-10", NULL};
mpv_command_async(mpv, 0, seek);
}
if(event.key.keysym.sym == SDLK_RIGHT)
{
const char *seek[] = {"seek", "+10", NULL};
mpv_command_async(mpv, 0, seek);
}
if (event.key.keysym.sym == SDLK_SPACE) {
const char *cmd_pause[] = {"cycle", "pause", NULL};
mpv_command_async(mpv, 0, cmd_pause);
}
break;
}
return EventResult::Handled;
}
void VideoPlayerWidget::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(window == nullptr) return;
int w=rect.w, h=rect.h;
if (!tex || tex_w != w || tex_h != h) {
SDL_DestroyTexture(tex);
tex = SDL_CreateTexture(window->GetRenderer(), SDL_PIXELFORMAT_RGBX8888,
SDL_TEXTUREACCESS_STREAMING, w, h);
if (!tex) {
printf("could not allocate texture\n");
exit(1);
}
tex_w = w;
tex_h = h;
}
void *pixels;
int pitch;
if (SDL_LockTexture(tex, NULL, &pixels, &pitch)) {
printf("could not lock texture\n");
exit(1);
}
size_t pitch1 = (size_t)pitch;
mpv_render_param params[] = {
{MPV_RENDER_PARAM_SW_SIZE, (int[2]){w, h}},
{MPV_RENDER_PARAM_SW_FORMAT, (void*)"0bgr"},
{MPV_RENDER_PARAM_SW_STRIDE, &pitch1},
{MPV_RENDER_PARAM_SW_POINTER, pixels},
{(mpv_render_param_type)0}
};
int r = mpv_render_context_render(mpv_rd, params);
if (r < 0) {
printf("mpv_render_context_render error: %s\n",
mpv_error_string(r));
exit(1);
}
SDL_UnlockTexture(tex);
if(tex)
SDL_RenderCopy(window->GetRenderer(), tex, NULL, &rect);
SDL_RenderPresent(window->GetRenderer());
}
void VideoPlayerWidget::LoadFile(std::string file)
{
const char *cmd[] = {"loadfile", file.c_str(), NULL};
mpv_command_async(mpv, 0, cmd);
}
VideoPlayerWidget::~VideoPlayerWidget()
{
if(tex != NULL)
SDL_DestroyTexture(tex);
// Destroy the GL renderer and all of the GL objects it allocated. If video
// is still running, the video track will be deselected.
mpv_render_context_free(mpv_rd);
mpv_destroy(mpv);
}
mpv_handle* VideoPlayerWidget::GetMPVHandle()
{
return this->mpv;
}
}

217
src/bigscreenwindow.cpp Normal file
View File

@@ -0,0 +1,217 @@
#include "bigscreen.hpp"
#include <iostream>
namespace Tesses::BigScreen
{
bool Widget::CanFocus()
{
return true;
}
void BigScreenWindow::DrawRectThick(SDL_Rect rect, int sz)
{
for(int i = 0; i < sz; i++)
{
SDL_RenderDrawRect(renderer,&rect);
rect.x++;
rect.y++;
rect.w-=2;
rect.h-=2;
}
}
int Widget::MinWidth()
{
return 0;
}
int Widget::MinHeight()
{
return 0;
}
BigScreenWindow::BigScreenWindow()
{
window = SDL_CreateWindow("Tesses Big Screen", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
this->fc = std::make_shared<FontCache>(renderer, 24);
running=true;
}
std::shared_ptr<FontCache> BigScreenWindow::GetFont()
{
return this->fc;
}
bool BigScreenWindow::IsCurrent(Widget* widget)
{
return this->current == widget;
}
void BigScreenWindow::SetCurrent(Widget* widget)
{
this->current = widget;
}
void BigScreenWindow::Run()
{
while(running)
{
SDL_Event event;
Tesses::Framework::TF_RunEventLoopItteration();
while(SDL_PollEvent(&event))
{
SDL_Rect rect={.x=0,.y=0,.w=0,.h=0};
SDL_GetWindowSize(window, &rect.w, &rect.h);
this->Event(event,rect);
}
SDL_Rect rect2={.x=0,.y=0,.w=0,.h=0};
SDL_GetWindowSize(window, &rect2.w, &rect2.h);
SDL_SetRenderDrawColor(renderer,color.r,color.g,color.b,color.a);
SDL_RenderClear(renderer);
this->Draw(rect2);
SDL_RenderPresent(renderer);
}
}
void BigScreenWindow::Draw(SDL_Rect& rect)
{
auto page = this->page;
if(page) page->Draw(rect);
}
EventResult BigScreenWindow::Event(SDL_Event& event, SDL_Rect& rect)
{
auto page = this->page;
if(this->current == nullptr && page)
this->current = page.get();
switch(event.type)
{
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_DOWN:
dir = LastDirection::South;
break;
case SDL_SCANCODE_UP:
dir = LastDirection::North;
break;
case SDL_SCANCODE_LEFT:
dir = LastDirection::West;
break;
case SDL_SCANCODE_RIGHT:
dir = LastDirection::East;
break;
default:
break;
}
}
break;
}
switch(event.type)
{
case SDL_QUIT:
{
running=false;
return EventResult::Handled;
}
default:
if(page)
{
return page->Event(event,rect);
}
break;
/*case SDL_KEYUP:
{
if(event.key.keysym.scancode == SDL_SCANCODE_F)
{
auto winFlags =SDL_GetWindowFlags(window);
if(winFlags & SDL_WINDOW_FULLSCREEN)
{
SDL_SetWindowFullscreen(window, 0);
}
else {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
}
}
}
break;
*/
}
return EventResult::Ignored;
}
SDL_Window* BigScreenWindow::GetWindow()
{
return this->window;
}
SDL_Renderer* BigScreenWindow::GetRenderer()
{
return this->renderer;
}
void BigScreenWindow::SetWidget(std::shared_ptr<Widget> page)
{
this->page = page;
if(page)
page->parent = this->weak_from_this();
}
BigScreenWindow::~BigScreenWindow()
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
}
std::shared_ptr<BigScreenWindow> Widget::GetRoot()
{
auto p = this->parent.lock();
if(p)
return p->GetRoot();
return std::shared_ptr<BigScreenWindow>();
}
std::shared_ptr<BigScreenWindow> BigScreenWindow::GetRoot()
{
auto mytype = this->shared_from_this();
if(mytype)
return mytype;
return std::shared_ptr<BigScreenWindow>();
}
}

204
src/button.cpp Normal file
View File

@@ -0,0 +1,204 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen
{
/*Button::Button(std::string text, std::function<void(std::shared_ptr<Widget> w)> cb) : Button(std::make_shared<Text>(text), cb)
{
}
Button::Button(std::shared_ptr<Widget> widget, std::function<void(std::shared_ptr<Widget> w)> cb) : child(widget), cb(cb)
{
}*/
Button::Button(std::string text, std::function<void(std::shared_ptr<Widget> parent)> cb) : child(std::make_shared<Text>(text)), color(COLOR_PRIMARY), cb(cb)
{
}
Button::Button(std::string text, std::string textColor, std::function<void(std::shared_ptr<Widget> parent)> cb): child(std::make_shared<Text>(text, textColor)), color(COLOR_PRIMARY), cb(cb)
{
}
Button::Button(std::string text, std::string textColor, std::string backColor, std::function<void(std::shared_ptr<Widget> parent)> cb): child(std::make_shared<Text>(text, textColor)), cb(cb)
{
SetBackColor(backColor);
}
Button::Button(std::string text, SDL_Color textColor, std::function<void(std::shared_ptr<Widget> parent)> cb): child(std::make_shared<Text>(text,textColor)), color(COLOR_PRIMARY), cb(cb)
{
}
Button::Button(std::string text, SDL_Color textColor, SDL_Color backColor, std::function<void(std::shared_ptr<Widget> parent)> cb) : child(std::make_shared<Text>(text,textColor)), color(backColor), cb(cb)
{
}
Button::Button(std::shared_ptr<Widget> widget, std::function<void(std::shared_ptr<Widget> parent)> cb) : child(widget), color(COLOR_PRIMARY), cb(cb)
{
}
Button::Button(std::shared_ptr<Widget> widget, std::string backColor, std::function<void(std::shared_ptr<Widget> parent)> cb) : child(widget), cb(cb)
{
SetBackColor(backColor);
}
Button::Button(std::shared_ptr<Widget> widget, SDL_Color backColor, std::function<void(std::shared_ptr<Widget> parent)> cb): child(widget), color(backColor), cb(cb)
{
}
SDL_Color Button::GetBackColor()
{
return this->color;
}
void Button::SetBackColor(std::string color)
{
if(!TryParseSDLColor(color,this->color))
this->color = COLOR_PRIMARY;
}
void Button::SetBackColor(SDL_Color color)
{
this->color = color;
}
void Button::SetWidget(std::shared_ptr<Widget> widget)
{
this->child = widget;
if(this->child)
this->child->parent = weak_from_this();
}
std::shared_ptr<Widget> Button::GetWidget()
{
return this->child;
}
EventResult Button::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
if(this->child && this->child->parent.use_count() == 0)
this->child->parent = weak_from_this();
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)
{
if(this->cb) this->cb(this->shared_from_this());
window->SetCurrent(this);
return EventResult::Handled;
}
}
break;
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_Scancode::SDL_SCANCODE_SPACE:
{
if(window->IsCurrent(this))
{
if(cb) cb(this->shared_from_this());
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;
}
void Button::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
if(this->child && this->child->parent.use_count() == 0)
this->child->parent = weak_from_this();
SDL_SetRenderDrawColor(window->GetRenderer(), color.r, color.g, color.b, 255);
SDL_RenderFillRect(window->GetRenderer(), &rect);
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;
}
if(child)
{
Clipper clip(window->GetRenderer(), rect);
int childWidth = child->MinWidth();
int childHeight = child->MinHeight();
SDL_Rect r2 = {
.x = rect.x + ((rect.w/2) - (childWidth/2)),
.y = rect.y + ((rect.h/2) - (childHeight/2)),
.w = childWidth,
.h = childHeight
};
if(clip.Clip(r2))
{
child->Draw(r2);
}
}
}
Button::~Button()
{
}
int Button::MinWidth()
{
if(this->child && this->child->parent.use_count() == 0)
this->child->parent = weak_from_this();
if(this->child)
return this->child->MinWidth() + 24;
return 128;
}
int Button::MinHeight()
{
if(this->child && this->child->parent.use_count() == 0)
this->child->parent = weak_from_this();
if(this->child)
return this->child->MinHeight() + 24;
return 32;
}
}

47
src/clipper.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen
{
Clipper::Clipper(SDL_Renderer* renderer, SDL_Rect& myRect)
{
this->isClipped=SDL_RenderIsClipEnabled(renderer);
SDL_RenderGetClipRect(renderer,&this->theRect);
if(!this->isClipped) this->theRect = myRect;
this->renderer = renderer;
}
void Clipper::ClipRect(SDL_Rect& child, SDL_Rect& parent)
{
if(child.x < parent.x)
{
int rem = parent.x- child.x;
child.x += rem;
child.w -= rem;
}
if(child.y < parent.y)
{
int rem = parent.y- child.y;
child.y += rem;
child.h -= rem;
}
if((child.x + child.w) > (parent.x + parent.w))
child.w = (parent.x + parent.w) - child.x;
if((child.y + child.h) > (parent.y + parent.h))
child.h = (parent.y + parent.h) - child.y;
}
bool Clipper::Clip(SDL_Rect rect)
{
ClipRect(rect,this->theRect);
if(rect.w <= 0 || rect.h <= 0) return false;
SDL_RenderSetClipRect(renderer, &rect);
return true;
}
Clipper::~Clipper()
{
if(isClipped) SDL_RenderSetClipRect(renderer, &this->theRect);
else SDL_RenderSetClipRect(renderer, nullptr);
}
}

274
src/font.cpp Normal file
View File

@@ -0,0 +1,274 @@
#include "bigscreen.hpp"
#include "font.h"
using HU= Tesses::Framework::Http::HttpUtils;
namespace Tesses::BigScreen
{
void FontCache::Load(SDL_Renderer* renderer,TTF_Font* font)
{
this->mw=0;
this->mh=0;
this->ps=ps;
for(size_t i = 0; i < this->font_chrs.size();i++)
{
SDL_Surface* surf = TTF_RenderGlyph_Blended(font,(Uint16)(i+32),{.r=255,.g=255,.b=255,.a=255});
if(surf->w > this->mw) mw = surf->w;
if(surf->h > this->mh) mh = surf->h;
this->font_chrs[i] = SDL_CreateTextureFromSurface(renderer,surf);
SDL_FreeSurface(surf);
}
}
FontCache::FontCache(SDL_Renderer* renderer,std::string font,int sz)
{
TTF_Font* f = TTF_OpenFont(font.c_str(),sz);
Load(renderer,f);
TTF_CloseFont(f);
}
FontCache::FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz)
{
TTF_Font* f = TTF_OpenFontRW(SDL_RWFromConstMem(mem,cnt),1,sz);
Load(renderer,f);
TTF_CloseFont(f);
}
FontCache::FontCache(SDL_Renderer* renderer,const std::vector<uint8_t>& v,int sz) : FontCache(renderer,v.data(),v.size(),sz)
{
}
FontCache::FontCache(SDL_Renderer* renderer,TTF_Font* font)
{
this->Load(renderer,font);
}
FontCache::FontCache(SDL_Renderer* renderer,int sz) : FontCache(renderer,TANOHESANSREGULAR_data,TANOHESANSREGULAR_length,sz)
{
}
void FontCache::CalculateSize(std::string text, int& x,int& y)
{
int myX = 0;
x=0;
y=0;
int maxH = MaxHeight();
y=maxH;
for(auto c : text)
{
switch(c)
{
case '\n':
{
y += maxH;
if(myX > x) x = myX;
myX = 0;
}
break;
case '\t':
{
auto tex = operator[](' ');
int wi;
SDL_QueryTexture(tex,NULL,NULL,&wi,NULL);
myX += wi * 4;
}
break;
default:
{
auto tex = operator[](c);
int wi;
int he;
SDL_QueryTexture(tex,NULL,NULL,&wi,&he);
myX += wi;
}
break;
}
}
if(myX > x) x = myX;
}
void FontCache::Render(SDL_Renderer* renderer,int x,int y, std::string text,const SDL_Color& color)
{
int myX = x;
int maxH = MaxHeight();
for(auto c : text)
{
switch(c)
{
case '\n':
{
y += maxH;
myX = x;
}
break;
case '\t':
{
auto tex = GetCharOfColor(' ',color);
int wi;
SDL_QueryTexture(tex,NULL,NULL,&wi,NULL);
myX += wi * 4;
}
break;
default:
{
auto tex = GetCharOfColor(c,color);
int wi;
int he;
SDL_QueryTexture(tex,NULL,NULL,&wi,&he);
SDL_Rect src={.x=0,.y=0,.w=wi,.h=he};
SDL_Rect dest={.x=myX,.y=y,.w=wi,.h=he};
myX += wi;
SDL_RenderCopy(renderer,tex,&src,&dest);
}
break;
}
}
}
SDL_Texture* FontCache::GetCharOfColor(char c, const SDL_Color& color)
{
auto res = (c >= 32 && c <= 126) ? this->font_chrs[c-32] : this->font_chrs[95];
SDL_SetTextureColorMod(res,color.r,color.g,color.b);
return res;
}
SDL_Texture* FontCache::operator[](char c)
{
return GetCharOfColor(c,{.r=255,.g=255,.b=255,.a=255});
}
FontCache::~FontCache()
{
for(auto item : this->font_chrs)
SDL_DestroyTexture(item);
}
int FontCache::MaxWidth()
{
return this->mw;
}
int FontCache::MaxHeight()
{
return this->mh;
}
int FontCache::PointSize()
{
return this->ps;
}
bool TryParseSDLColor(std::string str, SDL_Color& col)
{
str = HU::Replace(str," ","");
//rgba(255,255,255,1.0)
//rgb(197, 30, 30)
//#rgb
//#rgba
//#rrggbb
//#rrggbbaa
if(str.size() >= 4 && str[0] == '#')
{
if(str.size() == 4)
{
auto r = HU::HexToNibble(str[1]);
r |= r << 4;
auto g = HU::HexToNibble(str[2]);
g |= g << 4;
auto b = HU::HexToNibble(str[3]);
b |= b << 4;
col.r = r;
col.g = g;
col.b = b;
col.a = 255;
return true;
}
else if(str.size() == 5)
{
auto r = HU::HexToNibble(str[1]);
r |= r << 4;
auto g = HU::HexToNibble(str[2]);
g |= g << 4;
auto b = HU::HexToNibble(str[3]);
b |= b << 4;
auto a = HU::HexToNibble(str[4]);
a |= a << 4;
col.r = r;
col.g = g;
col.b = b;
col.a = a;
return true;
}
else if(str.size() == 7) {
auto r = HU::HexToNibble(str[1]);
r |= HU::HexToNibble(str[2]) << 4;
auto g = HU::HexToNibble(str[3]);
g |= HU::HexToNibble(str[4]) << 4;
auto b = HU::HexToNibble(str[5]);
b |= HU::HexToNibble(str[6]) << 4;
col.r = r;
col.g = g;
col.b = b;
col.a = 255;
return true;
}
else if(str.size() == 9)
{
auto r = HU::HexToNibble(str[1]);
r |= HU::HexToNibble(str[2]) << 4;
auto g = HU::HexToNibble(str[3]);
g |= HU::HexToNibble(str[4]) << 4;
auto b = HU::HexToNibble(str[5]);
b |= HU::HexToNibble(str[6]) << 4;
auto a = HU::HexToNibble(str[7]);
a |= HU::HexToNibble(str[8]) << 4;
col.r = r;
col.g = g;
col.b = b;
col.a = a;
return true;
}
}
else if(str.size() > 4 && strncmp(str.c_str(),"rgb(",4) == 0 && str[str.size()-1] == ')')
{
str = HU::Replace(str.substr(4),")","");
auto parts = HU::SplitString(str,",");
if(parts.size() != 3) return false;
try{
auto r = std::stoul(parts[0]);
auto g = std::stoul(parts[1]);
auto b = std::stoul(parts[2]);
col.r = (Uint8)r;
col.g = (Uint8)g;
col.b = (Uint8)b;
col.a = 255;
} catch(...) {
return false;
}
return true;
}
else if(str.size() > 5 && strncmp(str.c_str(),"rgba(",5) == 0 && str[str.size()-1] == ')')
{
str = HU::Replace(str.substr(5),")","");
auto parts = HU::SplitString(str,",");
if(parts.size() != 4) return false;
try{
auto r = std::stoul(parts[0]);
auto g = std::stoul(parts[1]);
auto b = std::stoul(parts[2]);
double a= std::stod(parts[3]);
col.r = (Uint8)r;
col.g = (Uint8)g;
col.b = (Uint8)b;
col.a = (Uint8)(a * 255);
} catch(...) {
return false;
}
return true;
}
return false;
}
}

353
src/hgrid.cpp Normal file
View File

@@ -0,0 +1,353 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen
{
void HGrid::AddChild(std::shared_ptr<Widget> widget, int width)
{
if(widget)
widget->parent = this->weak_from_this();
this->widgets.emplace_back(widget, width);
}
EventResult HGrid::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
if(window->IsCurrent(this) && !this->widgets.empty())
{
if(window->dir == LastDirection::West)
{
for(auto wb = this->widgets.rbegin(); wb != this->widgets.rend(); wb++)
{
if(wb->first && wb->first->CanFocus())
{
window->SetCurrent(wb->first.get());
break;
}
}
}
else {
for(auto wb = this->widgets.begin(); wb != this->widgets.end(); wb++)
{
if(wb->first && wb->first->CanFocus())
{
window->SetCurrent(wb->first.get());
break;
}
}
}
}
if(event.type == SDL_MOUSEBUTTONDOWN)
{
if(event.button.x < rect.x) return EventResult::Ignored;
if(event.button.y < rect.y) return EventResult::Ignored;
if(event.button.x >= rect.x+rect.w) return EventResult::Ignored;
if(event.button.y >= rect.y+rect.h) return EventResult::Ignored;
}
int absoluteWidths = 0;
int sections = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteWidths += std::max(this->widgets[i].first->MinWidth(), this->widgets[i].second);
}
else {
absoluteWidths += this->widgets[i].second;
}
}
else {
sections += (-(this->widgets[i].second));
}
}
int remainingWidths = rect.w - absoluteWidths;
int unitSz = 0;
if(remainingWidths > 0 && sections > 0)
{
unitSz = remainingWidths / sections;
}
int xbegin = rect.x;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
int width = this->widgets[i].second;
if(this->widgets[i].first)
{
auto myWidth = this->widgets[i].first->MinWidth();
if(myWidth > width)
width = myWidth;
SDL_Rect theRect = {
.x = xbegin,
.y = rect.y,
.w = width,
.h = rect.h
};
switch(this->widgets[i].first->Event(event,theRect))
{
case EventResult::Handled:
return EventResult::Handled;
case EventResult::Ignored:
break;
case EventResult::ParentDo:
{
switch(event.type)
{
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_RIGHT:
{
int j = i;
while(j + 1 < this->widgets.size())
{
if(this->widgets[j+1].first && this->widgets[j+1].first->CanFocus())
{
window->SetCurrent(this->widgets[j+1].first.get());
return EventResult::Handled;
}
j++;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_LEFT:
{
int j = i;
while(j > 0)
{
if(this->widgets[j-1].first && this->widgets[j-1].first->CanFocus())
{
window->SetCurrent(this->widgets[j-1].first.get());
return EventResult::Handled;
}
j--;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
return EventResult::ParentDo;
}
}
}
}
break;
}
}
xbegin += width;
}
else if(unitSz > 0) {
int units = (-(this->widgets[i].second));
int width = unitSz * units;
if(this->widgets[i].first)
{
SDL_Rect theRect = {
.x = xbegin,
.y = rect.y,
.w = width,
.h = rect.h
};
switch(this->widgets[i].first->Event(event,theRect))
{
case EventResult::Handled:
return EventResult::Handled;
case EventResult::Ignored:
break;
case EventResult::ParentDo:
{
switch(event.type)
{
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_RIGHT:
{
int j = i;
while(j + 1 < this->widgets.size())
{
if(this->widgets[j+1].first && this->widgets[j+1].first->CanFocus())
{
window->SetCurrent(this->widgets[j+1].first.get());
return EventResult::Handled;
}
j++;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_LEFT:
{
int j = i;
while(j > 0)
{
if(this->widgets[j-1].first && this->widgets[j-1].first->CanFocus())
{
window->SetCurrent(this->widgets[j-1].first.get());
return EventResult::Handled;
}
j--;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
return EventResult::ParentDo;
}
}
}
}
break;
}
}
xbegin += width;
}
}
return EventResult::Ignored;
}
void HGrid::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
int absoluteWidths = 0;
int sections = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteWidths += std::max(this->widgets[i].first->MinWidth(), this->widgets[i].second);
}else {
absoluteWidths += this->widgets[i].second;
}
}
else {
sections += (-(this->widgets[i].second));
}
}
int remainingWidths = rect.w - absoluteWidths;
int unitSz = 0;
if(remainingWidths > 0 && sections > 0)
{
unitSz = remainingWidths / sections;
}
Clipper clipper(window->GetRenderer(), rect);
int xbegin = rect.x;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
int width = this->widgets[i].second;
if(this->widgets[i].first)
{
auto myWidth = this->widgets[i].first->MinWidth();
if(myWidth > width)
width = myWidth;
SDL_Rect theRect = {
.x = xbegin,
.y = rect.y,
.w = width,
.h = rect.h
};
clipper.Clip(theRect);
this->widgets[i].first->Draw(theRect);
}
xbegin += width;
}
else if(unitSz > 0) {
int units = (-(this->widgets[i].second));
int width = unitSz * units;
if(this->widgets[i].first)
{
SDL_Rect theRect = {
.x = xbegin,
.y = rect.y,
.w = width,
.h = rect.h
};
clipper.Clip(theRect);
this->widgets[i].first->Draw(theRect);
}
xbegin += width;
}
}
}
int HGrid::MinWidth()
{
int absoluteWidths = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteWidths += std::max(this->widgets[i].first->MinWidth(), this->widgets[i].second);
}
else {
absoluteWidths += this->widgets[i].second;
}
}
}
return absoluteWidths;
}
int HGrid::MinHeight()
{
int height = 0;
for(auto& item : this->widgets)
{
if(item.first)
{
auto he = item.first->MinHeight();
if(he > height) height = he;
}
}
return height;
}
}

57
src/main.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "bigscreen.hpp"
int main(int argc, char** argv)
{
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);
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;
}

85
src/text.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen {
Text::Text(std::string text) : Text(text, COLOR_TEXT)
{
}
Text::Text(std::string text, SDL_Color color): text(text), color(color)
{
}
Text::Text(std::string text,std::string color): text(text)
{
SetColor(color);
}
SDL_Color Text::GetColor()
{
return this->color;
}
void Text::SetColor(SDL_Color color)
{
this->color = color;
}
void Text::SetColor(std::string color)
{
if(!TryParseSDLColor(color, this->color))
this->color= COLOR_TEXT;
}
std::string Text::GetText()
{
return text;
}
void Text::SetText(std::string text)
{
this->text = text;
}
bool Text::CanFocus()
{
return false;
}
EventResult Text::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(window && window->IsCurrent(this))
{
return EventResult::ParentDo;
}
return EventResult::Ignored;
}
void Text::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
Clipper clipper(window->GetRenderer(),rect);
if(clipper.Clip(rect))
{
window->GetFont()->Render(window->GetRenderer(),rect.x,rect.y,text,color);
}
}
int Text::MinWidth()
{
auto window = this->GetRoot();
if(!window) return 0;
int x=0;
int y=0;
window->GetFont()->CalculateSize(text,x,y);
return x;
}
int Text::MinHeight()
{
auto window = this->GetRoot();
if(!window) return 0;
int x=0;
int y=0;
window->GetFont()->CalculateSize(text,x,y);
return y;
}
}

353
src/vgrid.cpp Normal file
View File

@@ -0,0 +1,353 @@
#include "bigscreen.hpp"
namespace Tesses::BigScreen
{
void VGrid::AddChild(std::shared_ptr<Widget> widget, int height)
{
if(widget)
widget->parent = this->weak_from_this();
this->widgets.emplace_back(widget, height);
}
EventResult VGrid::Event(SDL_Event& event,SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return EventResult::Ignored;
if(window->IsCurrent(this) && !this->widgets.empty())
{
if(window->dir == LastDirection::North)
{
for(auto wb = this->widgets.rbegin(); wb != this->widgets.rend(); wb++)
{
if(wb->first && wb->first->CanFocus())
{
window->SetCurrent(wb->first.get());
break;
}
}
}
else {
for(auto wb = this->widgets.begin(); wb != this->widgets.end(); wb++)
{
if(wb->first && wb->first->CanFocus())
{
window->SetCurrent(wb->first.get());
break;
}
}
}
}
if(event.type == SDL_MOUSEBUTTONDOWN)
{
if(event.button.x < rect.x) return EventResult::Ignored;
if(event.button.y < rect.y) return EventResult::Ignored;
if(event.button.x >= rect.x+rect.w) return EventResult::Ignored;
if(event.button.y >= rect.y+rect.h) return EventResult::Ignored;
}
int absoluteHeights = 0;
int sections = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteHeights += std::max(this->widgets[i].first->MinHeight(), this->widgets[i].second);
}
else {
absoluteHeights += this->widgets[i].second;
}
}
else {
sections += (-(this->widgets[i].second));
}
}
int remainingHeights = rect.h - absoluteHeights;
int unitSz = 0;
if(remainingHeights > 0 && sections > 0)
{
unitSz = remainingHeights / sections;
}
int ybegin = rect.y;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
int height = this->widgets[i].second;
if(this->widgets[i].first)
{
auto myHeight = this->widgets[i].first->MinHeight();
if(myHeight > height)
height = myHeight;
SDL_Rect theRect = {
.x = rect.x,
.y = ybegin,
.w = rect.w,
.h = height
};
switch(this->widgets[i].first->Event(event,theRect))
{
case EventResult::Handled:
return EventResult::Handled;
case EventResult::Ignored:
break;
case EventResult::ParentDo:
{
switch(event.type)
{
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_DOWN:
{
int j = i;
while(j + 1 < this->widgets.size())
{
if(this->widgets[j+1].first && this->widgets[j+1].first->CanFocus())
{
window->SetCurrent(this->widgets[j+1].first.get());
return EventResult::Handled;
}
j++;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_UP:
{
int j = i;
while(j > 0)
{
if(this->widgets[j-1].first && this->widgets[j-1].first->CanFocus())
{
window->SetCurrent(this->widgets[j-1].first.get());
return EventResult::Handled;
}
j--;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_RIGHT:
return EventResult::ParentDo;
}
}
}
}
break;
}
}
ybegin += height;
}
else if(unitSz > 0) {
int units = (-(this->widgets[i].second));
int height = unitSz * units;
if(this->widgets[i].first)
{
SDL_Rect theRect = {
.x = rect.x,
.y = ybegin,
.w = rect.w,
.h = height
};
switch(this->widgets[i].first->Event(event,theRect))
{
case EventResult::Handled:
return EventResult::Handled;
case EventResult::Ignored:
break;
case EventResult::ParentDo:
{
switch(event.type)
{
case SDL_KEYDOWN:
{
switch(event.key.keysym.scancode)
{
case SDL_SCANCODE_DOWN:
{
int j = i;
while(j + 1 < this->widgets.size())
{
if(this->widgets[j+1].first && this->widgets[j+1].first->CanFocus())
{
window->SetCurrent(this->widgets[j+1].first.get());
return EventResult::Handled;
}
j++;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_UP:
{
int j = i;
while(j > 0)
{
if(this->widgets[j-1].first && this->widgets[j-1].first->CanFocus())
{
window->SetCurrent(this->widgets[j-1].first.get());
return EventResult::Handled;
}
j--;
}
return EventResult::ParentDo;
}
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_RIGHT:
return EventResult::ParentDo;
}
}
}
}
break;
}
}
ybegin += height;
}
}
return EventResult::Ignored;
}
void VGrid::Draw(SDL_Rect& rect)
{
auto window = this->GetRoot();
if(!window) return;
int absoluteHeights = 0;
int sections = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteHeights += std::max(this->widgets[i].first->MinHeight(), this->widgets[i].second);
}
else {
absoluteHeights += this->widgets[i].second;
}
}
else {
sections += (-(this->widgets[i].second));
}
}
int remainingHeights = rect.h - absoluteHeights;
int unitSz = 0;
if(remainingHeights > 0 && sections > 0)
{
unitSz = remainingHeights / sections;
}
Clipper clipper(window->GetRenderer(), rect);
int ybegin = rect.y;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
int height = this->widgets[i].second;
if(this->widgets[i].first)
{
auto myHeight = this->widgets[i].first->MinHeight();
if(myHeight > height)
height = myHeight;
SDL_Rect theRect = {
.x = rect.x,
.y = ybegin,
.w = rect.w,
.h = height
};
clipper.Clip(theRect);
this->widgets[i].first->Draw(theRect);
}
ybegin += height;
}
else if(unitSz > 0) {
int units = (-(this->widgets[i].second));
int height = unitSz * units;
if(this->widgets[i].first)
{
SDL_Rect theRect = {
.x = rect.x,
.y = ybegin,
.w = rect.w,
.h = height
};
clipper.Clip(theRect);
this->widgets[i].first->Draw(theRect);
}
ybegin += height;
}
}
}
int VGrid::MinWidth()
{
int width = 0;
for(auto& item : this->widgets)
{
if(item.first)
{
auto wi = item.first->MinWidth();
if(wi > width) width = wi;
}
}
return width;
}
int VGrid::MinHeight()
{
int absoluteHeights = 0;
for(size_t i = 0; i < this->widgets.size(); i++)
{
if(this->widgets[i].second >= 0)
{
if(this->widgets[i].first)
{
absoluteHeights += std::max(this->widgets[i].first->MinHeight(), this->widgets[i].second);
}
else {
absoluteHeights += this->widgets[i].second;
}
}
}
return absoluteHeights;
}
}

13
tmp/file.tcross Normal file
View File

@@ -0,0 +1,13 @@
func Layout()
{
const tb = TextBox();
return VGrid(
[
[Text("Hello, world"),0]
[tb,0],
[Button("My Button", (btn)=>{}),0]
]
);
}