First commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build
|
||||
82
CMakeLists.txt
Normal file
82
CMakeLists.txt
Normal 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
254
include/bigscreen.hpp
Normal 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
12
include/font.h
Normal file
File diff suppressed because one or more lines are too long
143
src/bigscreenplayer.cpp
Normal file
143
src/bigscreenplayer.cpp
Normal 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
217
src/bigscreenwindow.cpp
Normal 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
204
src/button.cpp
Normal 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
47
src/clipper.cpp
Normal 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
274
src/font.cpp
Normal 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
353
src/hgrid.cpp
Normal 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
57
src/main.cpp
Normal 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
85
src/text.cpp
Normal 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
353
src/vgrid.cpp
Normal 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
13
tmp/file.tcross
Normal file
@@ -0,0 +1,13 @@
|
||||
func Layout()
|
||||
{
|
||||
const tb = TextBox();
|
||||
|
||||
|
||||
return VGrid(
|
||||
[
|
||||
[Text("Hello, world"),0]
|
||||
[tb,0],
|
||||
[Button("My Button", (btn)=>{}),0]
|
||||
]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user