#include "Precompiled.h" #ifdef ATOMIC_TBUI #include "../Core/Context.h" #include "../Core/CoreEvents.h" #include "../Core/Profiler.h" #include "../IO/Log.h" #include "../IO/File.h" #include "../Resource/ResourceCache.h" #include "../Graphics/Graphics.h" #include "../Graphics/GraphicsEvents.h" #include "../Graphics/Texture2D.h" #include "../Graphics/VertexBuffer.h" #include "../Input/Input.h" #include "../Input/InputEvents.h" #include "../UI/UI.h" #include "../UI/TBUI.h" #include #include #include #include #include #include #include #include #include void register_tbbf_font_renderer(); void register_stb_font_renderer(); void register_freetype_font_renderer(); using namespace tb; namespace tb { void TBSystem::RescheduleTimer(double fire_time) { } } namespace Atomic { extern const char* UI_CATEGORY; static MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey) { MODIFIER_KEYS code = TB_MODIFIER_NONE; if (qualifiers & QUAL_ALT) code |= TB_ALT; if (qualifiers & QUAL_CTRL) code |= TB_CTRL; if (qualifiers & QUAL_SHIFT) code |= TB_SHIFT; if (superKey) code |= TB_SUPER; return code; } // @return Return the upper case of a ascii charcter. Only for shortcut handling. static int toupr_ascii(int ascii) { if (ascii >= 'a' && ascii <= 'z') return ascii + 'A' - 'a'; return ascii; } class TBUIRenderer; class TBUIBitmap : public tb::TBBitmap { public: TBUIBitmap(TBUIRenderer *renderer); virtual ~TBUIBitmap(); bool Init(int width, int height, tb::uint32 *data); virtual int Width() { return width_; } virtual int Height() { return height_; } virtual void SetData(tb::uint32 *data); public: TBUIRenderer *renderer_; int width_; int height_; SharedPtr texture_; }; TBUIBitmap::TBUIBitmap(TBUIRenderer *renderer) : renderer_(renderer), width_(0), height_(0) { } class TBUIRenderer : public TBRendererBatcher { public: WeakPtr context_; WeakPtr tbui_; PODVector* batches_; PODVector* vertexData_; IntRect currentScissor_; TBUIRenderer(Context* context) { context_ = context; } virtual ~TBUIRenderer() { } void BeginPaint(int render_target_w, int render_target_h) { TBRendererBatcher::BeginPaint(render_target_w, render_target_h); } void EndPaint() { TBRendererBatcher::EndPaint(); } TBBitmap *CreateBitmap(int width, int height, uint32 *data) { TBUIBitmap *bitmap = new TBUIBitmap(this); if (!bitmap->Init(width, height, data)) { delete bitmap; return nullptr; } return bitmap; } void RenderBatch(Batch *batch) { if (!batch->vertex_count) return; Texture2D* texture = NULL; if (batch->bitmap) { TBUIBitmap* tbuibitmap = (TBUIBitmap*)batch->bitmap; texture = tbuibitmap->texture_; } UIBatch b(tbui_, BLEND_ALPHA , currentScissor_, texture, vertexData_); float fadeAlpha = tbui_->GetFadeAlpha(); unsigned begin = b.vertexData_->Size(); b.vertexData_->Resize(begin + batch->vertex_count * UI_VERTEX_SIZE); float* dest = &(b.vertexData_->At(begin)); b.vertexEnd_ = b.vertexData_->Size(); // winding is reversed, however switching it causes rendering // issues, so turned off culling in UI module //for (int i = batch->vertex_count - 1; i >= 0; i--) for (int i = 0; i < batch->vertex_count; i++) { Vertex* v = &batch->vertex[i]; dest[0] = v->x; dest[1] = v->y; dest[2] = 0.0f; v->a *= fadeAlpha; ((unsigned&)dest[3]) = v->col; dest[4] = v->u; dest[5] = v->v; dest += UI_VERTEX_SIZE; } UIBatch::AddOrMerge(b, *batches_); } void SetClipRect(const TBRect &rect) { currentScissor_.top_ = rect.y; currentScissor_.bottom_ = rect.y + rect.h; currentScissor_.left_ = rect.x; currentScissor_.right_ = rect.x + rect.w; } static TBUIRenderer* renderer_; }; TBUIRenderer* TBUIRenderer::renderer_ = NULL; TBUIBitmap::~TBUIBitmap() { // Must flush and unbind before we delete the texture renderer_->FlushBitmap(this); } bool TBUIBitmap::Init(int width, int height, tb::uint32 *data) { assert(width == tb::TBGetNearestPowerOfTwo(width)); assert(height == tb::TBGetNearestPowerOfTwo(height)); width_ = width; height_ = height; texture_ = new Texture2D(renderer_->context_); texture_->SetMipsToSkip(QUALITY_LOW, 0); // No quality reduction texture_->SetNumLevels(1); texture_->SetAddressMode(COORD_U, ADDRESS_BORDER); texture_->SetAddressMode(COORD_V, ADDRESS_BORDER), texture_->SetBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f)); texture_->SetSize(width_, height_, Graphics::GetRGBAFormat(), TEXTURE_STATIC); SetData(data); return true; } void TBUIBitmap::SetData(tb::uint32 *data) { renderer_->FlushBitmap(this); texture_->SetData(0, 0, 0, width_, height_, data); } class TBUIRootWidget : public TBWidget { public: }; WeakPtr TBUI::readerContext_; TBUI::TBUI(Context* context) : UIElement(context), rootWidget_(0), initialized_(false), inputDisabled_(false), fadeAlpha_(1.0f), fadeTarget_(1.0f), currentFadeTime_(0.0f), fadeTime_(0.0f), shuttingDown_(false) { SubscribeToEvent(E_SCREENMODE, HANDLER(TBUI, HandleScreenMode)); } TBUI::~TBUI() { if (initialized_) { tb::TBWidgetsAnimationManager::Shutdown(); delete rootWidget_; // leak //delete TBUIRenderer::renderer_; tb_core_shutdown(); } } void TBUI::Initialize() { assert(!initialized_); Graphics* graphics = GetSubsystem(); assert(graphics); assert(graphics->IsInitialized()); readerContext_ = context_; TBFile::SetReaderFunction(TBFileReader); graphics_ = graphics; vertexBuffer_ = new VertexBuffer(context_); TBWidgetsAnimationManager::Init(); TBUIRenderer::renderer_ = new TBUIRenderer(graphics->GetContext()); TBUIRenderer::renderer_->tbui_ = this; tb_core_init(TBUIRenderer::renderer_, "AtomicEditor/resources/language/lng_en.tb.txt"); // Load the default skin, and override skin that contains the graphics specific to the demo. tb::g_tb_skin->Load("AtomicEditor/resources/default_skin/skin.tb.txt", "AtomicEditor/editor/skin/skin.tb.txt"); //register_tbbf_font_renderer(); //register_stb_font_renderer(); register_freetype_font_renderer(); tb::g_font_manager->AddFontInfo("AtomicEditor/resources/MesloLGS-Regular.ttf", "Monaco"); tb::g_font_manager->AddFontInfo("AtomicEditor/resources/vera.ttf", "Vera"); tb::TBFontDescription fd; fd.SetID(tb::TBIDC("Vera")); fd.SetSize(tb::g_tb_skin->GetDimensionConverter()->DpToPx(12)); tb::g_font_manager->SetDefaultFontDescription(fd); // Create the font now. tb::TBFontFace *font = tb::g_font_manager->CreateFontFace(tb::g_font_manager->GetDefaultFontDescription()); // Render some glyphs in one go now since we know we are going to use them. It would work fine // without this since glyphs are rendered when needed, but with some extra updating of the glyph bitmap. if (font) font->RenderGlyphs(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~•·åäöÅÄÖ"); rootWidget_ = new TBUIRootWidget(); //rootWidget_->SetSkinBg(tb::TBIDC("background")); int width = graphics->GetWidth(); int height = graphics->GetHeight(); rootWidget_->SetSize(width, height); SetSize(width, height); rootWidget_->SetVisibilility(tb::WIDGET_VISIBILITY_VISIBLE); SubscribeToEvent(E_MOUSEBUTTONDOWN, HANDLER(TBUI, HandleMouseButtonDown)); SubscribeToEvent(E_MOUSEBUTTONUP, HANDLER(TBUI, HandleMouseButtonUp)); SubscribeToEvent(E_MOUSEMOVE, HANDLER(TBUI, HandleMouseMove)); SubscribeToEvent(E_MOUSEWHEEL, HANDLER(TBUI, HandleMouseWheel)); SubscribeToEvent(E_KEYDOWN, HANDLER(TBUI, HandleKeyDown)); SubscribeToEvent(E_KEYUP, HANDLER(TBUI, HandleKeyUp)); SubscribeToEvent(E_TEXTINPUT, HANDLER(TBUI, HandleTextInput)); SubscribeToEvent(E_UPDATE, HANDLER(TBUI, HandleUpdate)); SubscribeToEvent(E_RENDERUPDATE, HANDLER(TBUI, HandleRenderUpdate)); //TB_DEBUG_SETTING(LAYOUT_BOUNDS) = 1; initialized_ = true; } void TBUI::Shutdown() { shuttingDown_ = true; SetInputDisabled(true); } void TBUI::GetBatches(PODVector& batches, PODVector& vertexData, const IntRect& currentScissor) { if (!initialized_) return; TBAnimationManager::Update(); rootWidget_->InvokeProcessStates(); rootWidget_->InvokeProcess(); if (fadeAlpha_ > 0.0f) { tb::g_renderer->BeginPaint(rootWidget_->GetRect().w, rootWidget_->GetRect().h); TBUIRenderer::renderer_->currentScissor_ = currentScissor; TBUIRenderer::renderer_->batches_ = &batches; TBUIRenderer::renderer_->vertexData_ = &vertexData; rootWidget_->InvokePaint(tb::TBWidget::PaintProps()); tb::g_renderer->EndPaint(); } } void TBUI::SubmitBatchVertexData(Texture* texture, const PODVector& vertexData) { UIBatch b(this, BLEND_ALPHA , TBUIRenderer::renderer_->currentScissor_, texture, &vertexData_); unsigned begin = b.vertexData_->Size(); b.vertexData_->Resize(begin + vertexData.Size()); float* dest = &(b.vertexData_->At(begin)); b.vertexEnd_ = b.vertexData_->Size(); for (unsigned i = 0; i < vertexData.Size(); i++, dest++) { *dest = vertexData[i]; } UIBatch::AddOrMerge(b, batches_); } void TBUI::HandleScreenMode(StringHash eventType, VariantMap& eventData) { if (!initialized_) return; using namespace ScreenMode; rootWidget_->SetSize(eventData[P_WIDTH].GetInt(), eventData[P_HEIGHT].GetInt()); SetSize(eventData[P_WIDTH].GetInt(), eventData[P_HEIGHT].GetInt()); } void TBUI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData) { if (!initialized_ || inputDisabled_) return; using namespace MouseButtonDown; unsigned button = eventData[P_BUTTON].GetUInt(); IntVector2 pos; pos = GetSubsystem()->GetCursorPosition(); Input* input = GetSubsystem(); int qualifiers = input->GetQualifiers(); #ifdef ATOMIC_PLATFORM_WINDOWS bool superdown = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL); #else bool superdown = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI); #endif MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown); static double last_time = 0; static int counter = 1; Time* t = GetSubsystem