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