diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 18a0fb7c5..5fcca8e89 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -948,7 +948,7 @@ void EditorComponent::Load() helpLabel.Create("HelpLabel"); helpLabel.SetText(ss); helpLabel.SetVisible(false); - helpLabel.SetColor(wi::Color(113, 183, 214, 100), wi::gui::WIDGETSTATE::IDLE); + helpLabel.SetColor(wi::Color(113, 183, 214, 100)); GetGUI().AddWidget(&helpLabel); } diff --git a/WickedEngine/wiFont.cpp b/WickedEngine/wiFont.cpp index cb8994025..3a9ffc644 100644 --- a/WickedEngine/wiFont.cpp +++ b/WickedEngine/wiFont.cpp @@ -98,40 +98,46 @@ namespace wi::font }; static wi::vector fontStyles; - template - uint32_t WriteVertices(FontVertex* vertexList, const T* text, Params params) + struct Cursor { - const FontStyle& fontStyle = fontStyles[params.style]; - const float fontScale = stbtt_ScaleForPixelHeight(&fontStyle.fontInfo, (float)params.size); - + XMFLOAT2 pos = {}; + XMFLOAT2 size = {}; uint32_t quadCount = 0; - float line = 0; - float pos = 0; - float pos_last_letter = 0; size_t last_word_begin = 0; bool start_new_word = false; + }; + + static thread_local wi::vector vertexList; + + template + Cursor ParseText(const T* text, size_t text_length, Params params) + { + Cursor cursor; + const FontStyle& fontStyle = fontStyles[params.style]; + const float fontScale = stbtt_ScaleForPixelHeight(&fontStyle.fontInfo, (float)params.size); + vertexList.clear(); auto word_wrap = [&] { - start_new_word = true; - if (last_word_begin > 0 && params.h_wrap >= 0 && pos >= params.h_wrap - 1) + cursor.start_new_word = true; + if (cursor.last_word_begin > 0 && params.h_wrap >= 0 && cursor.pos.x >= params.h_wrap - 1) { // Word ended and wrap detected, push down last word by one line: - float word_offset = vertexList[last_word_begin].pos.x; - for (size_t i = last_word_begin; i < quadCount * 4; ++i) + float word_offset = vertexList[cursor.last_word_begin].pos.x; + for (size_t i = cursor.last_word_begin; i < cursor.quadCount * 4; ++i) { vertexList[i].pos.x -= word_offset; vertexList[i].pos.y += LINEBREAK_SIZE; } - line += LINEBREAK_SIZE; - pos -= word_offset; + cursor.pos.x -= word_offset; + cursor.pos.y += LINEBREAK_SIZE; } }; + cursor.size.y = LINEBREAK_SIZE; int code_prev = 0; - size_t i = 0; - while (text[i] != 0) + for (size_t i = 0; i < text_length; ++i) { - T character = text[i++]; + T character = text[i]; int code = (int)character; const int32_t hash = glyphhash(code, params.style, params.size); @@ -147,20 +153,20 @@ namespace wi::font if (code == '\n') { word_wrap(); - line += LINEBREAK_SIZE; - pos = 0; + cursor.pos.x = 0; + cursor.pos.y += LINEBREAK_SIZE; code_prev = 0; } else if (code == ' ') { word_wrap(); - pos += WHITESPACE_SIZE; + cursor.pos.x += WHITESPACE_SIZE; code_prev = 0; } else if (code == '\t') { word_wrap(); - pos += TAB_SIZE; + cursor.pos.x += TAB_SIZE; code_prev = 0; } else @@ -171,24 +177,26 @@ namespace wi::font const float glyphOffsetX = glyph.x * params.scaling; const float glyphOffsetY = glyph.y * params.scaling; - const size_t vertexID = size_t(quadCount) * 4; + const size_t vertexID = size_t(cursor.quadCount) * 4; + vertexList.resize(vertexID + 4); + cursor.quadCount++; - if (start_new_word) + if (cursor.start_new_word) { - last_word_begin = vertexID; + cursor.last_word_begin = vertexID; } - start_new_word = false; + cursor.start_new_word = false; if (code_prev != 0) { int kern = stbtt_GetCodepointKernAdvance(&fontStyle.fontInfo, code_prev, code); - pos += kern * fontScale; + cursor.pos.x += kern * fontScale; } code_prev = code; - const float left = pos + glyphOffsetX; + const float left = cursor.pos.x + glyphOffsetX; const float right = left + glyphWidth; - const float top = line + glyphOffsetY; + const float top = cursor.pos.y + glyphOffsetY; const float bottom = top + glyphHeight; vertexList[vertexID + 0].pos = float2(left, top); @@ -201,17 +209,20 @@ namespace wi::font vertexList[vertexID + 2].uv = float2(glyph.tc_left, glyph.tc_bottom); vertexList[vertexID + 3].uv = float2(glyph.tc_right, glyph.tc_bottom); - pos += glyph.width * params.scaling + params.spacingX; - pos_last_letter = pos; - - quadCount++; + cursor.pos.x += glyph.width * params.scaling + params.spacingX; } + cursor.size.x = std::max(cursor.size.x, cursor.pos.x); + cursor.size.y = std::max(cursor.size.y, cursor.pos.y); } word_wrap(); - return quadCount; + return cursor; + } + void CommitText(void* vertexList_GPU) + { + std::memcpy(vertexList_GPU, vertexList.data(), sizeof(FontVertex) * vertexList.size()); } } @@ -423,76 +434,6 @@ namespace wi::font return int(fontStyles.size() - 1); } - - template - float TextWidth_internal(const T* text, const Params& params) - { - if (params.style >= (int)fontStyles.size()) - { - return 0; - } - - // TODO: account for word wrap - float maxWidth = 0; - float currentLineWidth = 0; - size_t i = 0; - while (text[i] != 0) - { - int code = (int)text[i++]; - const int32_t hash = glyphhash(code, params.style, params.size); - - if (glyph_lookup.count(hash) == 0) - { - // glyph not packed yet, we just continue (it will be added if it is actually rendered) - continue; - } - - if (code == '\n') - { - currentLineWidth = 0; - } - else if (code == ' ') - { - currentLineWidth += WHITESPACE_SIZE; - } - else if (code == '\t') - { - currentLineWidth += TAB_SIZE; - } - else - { - const Glyph& glyph = glyph_lookup.at(hash); - currentLineWidth += glyph.width + float(params.spacingX) * params.scaling; - } - maxWidth = std::max(maxWidth, currentLineWidth); - } - - return maxWidth; - } - - template - float TextHeight_internal(const T* text, const Params& params) - { - if (params.style >= (int)fontStyles.size()) - { - return 0; - } - - // TODO: account for word wrap - float height = LINEBREAK_SIZE; - size_t i = 0; - while (text[i] != 0) - { - int code = (int)text[i++]; - if (code == '\n') - { - height += LINEBREAK_SIZE; - } - } - - return height; - } - template void Draw_internal(const T* text, size_t text_length, const Params& params, CommandList cmd) { @@ -500,32 +441,29 @@ namespace wi::font { return; } + Cursor cursor = ParseText(text, text_length, params); Params newProps = params; - if (params.h_align == WIFALIGN_CENTER) - newProps.posX -= TextWidth_internal(text, newProps) / 2; + newProps.posX -= cursor.size.x / 2; else if (params.h_align == WIFALIGN_RIGHT) - newProps.posX -= TextWidth_internal(text, newProps); + newProps.posX -= cursor.size.x; if (params.v_align == WIFALIGN_CENTER) - newProps.posY -= TextHeight_internal(text, newProps) / 2; + newProps.posY -= cursor.size.y / 2; else if (params.v_align == WIFALIGN_BOTTOM) - newProps.posY -= TextHeight_internal(text, newProps); + newProps.posY -= cursor.size.y; - GraphicsDevice* device = wi::graphics::GetDevice(); - GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(FontVertex) * text_length * 4, cmd); - if (!mem.IsValid()) + if (cursor.quadCount > 0) { - return; - } - static thread_local wi::vector textbuffer; - textbuffer.resize(text_length * 4); - const uint32_t quadCount = WriteVertices(textbuffer.data(), text, newProps); - std::memcpy(mem.data, textbuffer.data(), textbuffer.size() * sizeof(FontVertex)); // only allow writes into mapped GPU buffer memory to avoid uncached read by mistake + GraphicsDevice* device = wi::graphics::GetDevice(); + GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(FontVertex) * cursor.quadCount * 4, cmd); + if (!mem.IsValid()) + { + return; + } + CommitText(mem.data); - if (quadCount > 0) - { device->EventBegin("Font", cmd); device->BindPipelineState(&PSO, cmd); @@ -555,7 +493,7 @@ namespace wi::font font_push.color = newProps.shadowColor.rgba; device->PushConstants(&font_push, sizeof(font_push), cmd); - device->DrawInstanced(4, quadCount, 0, 0, cmd); + device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd); } // font base render: @@ -568,7 +506,7 @@ namespace wi::font font_push.color = newProps.color.rgba; device->PushConstants(&font_push, sizeof(font_push), cmd); - device->DrawInstanced(4, quadCount, 0, 0, cmd); + device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd); device->EventEnd(cmd); } @@ -610,36 +548,72 @@ namespace wi::font float TextWidth(const char* text, const Params& params) { - return TextWidth_internal(text, params); + size_t text_length = strlen(text); + if (text_length == 0) + { + return 0; + } + return ParseText(text, text_length, params).size.x; } float TextWidth(const wchar_t* text, const Params& params) { - return TextWidth_internal(text, params); + size_t text_length = wcslen(text); + if (text_length == 0) + { + return 0; + } + return ParseText(text, text_length, params).size.x; } float TextWidth(const std::string& text, const Params& params) { - return TextWidth_internal(text.c_str(), params); + if (text.empty()) + { + return 0; + } + return ParseText(text.c_str(), text.length(), params).size.x; } float TextWidth(const std::wstring& text, const Params& params) { - return TextWidth_internal(text.c_str(), params); + if (text.empty()) + { + return 0; + } + return ParseText(text.c_str(), text.length(), params).size.x; } float TextHeight(const char* text, const Params& params) { - return TextHeight_internal(text, params); + size_t text_length = strlen(text); + if (text_length == 0) + { + return 0; + } + return ParseText(text, text_length, params).size.y; } float TextHeight(const wchar_t* text, const Params& params) { - return TextHeight_internal(text, params); + size_t text_length = wcslen(text); + if (text_length == 0) + { + return 0; + } + return ParseText(text, text_length, params).size.y; } float TextHeight(const std::string& text, const Params& params) { - return TextHeight_internal(text.c_str(), params); + if (text.empty()) + { + return 0; + } + return ParseText(text.c_str(), text.length(), params).size.y; } float TextHeight(const std::wstring& text, const Params& params) { - return TextHeight_internal(text.c_str(), params); + if (text.empty()) + { + return 0; + } + return ParseText(text.c_str(), text.length(), params).size.y; } } diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index f4cdf1ba6..8daf657aa 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -714,11 +714,190 @@ namespace wi::gui + + void ScrollBar::Update(const wi::Canvas& canvas, float dt) + { + if (!IsVisible()) + { + return; + } + + Widget::Update(canvas, dt); + + if (scale.x > scale.y) + { + vertical = false; + } + else + { + vertical = true; + } + + if (IsEnabled()) + { + if (state == FOCUS) + { + state = IDLE; + } + if (state == DEACTIVATING) + { + state = IDLE; + } + if (state == ACTIVE) + { + Deactivate(); + } + + Hitbox2D hitbox = Hitbox2D(XMFLOAT2(translation.x, translation.y), XMFLOAT2(scale.x, scale.y)); + Hitbox2D pointerHitbox = GetPointerHitbox(); + + if (state == IDLE && hitbox.intersects(pointerHitbox)) + { + state = FOCUS; + } + + bool clicked = false; + if (wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)) + { + clicked = true; + } + + bool click_down = false; + if (wi::input::Down(wi::input::MOUSE_BUTTON_LEFT)) + { + click_down = true; + if (state == FOCUS || state == DEACTIVATING) + { + // Keep pressed until mouse is released + Activate(); + } + } + float scrollbar_begin; + float scrollbar_end; + float scrollbar_size; + + if (vertical) + { + scrollbar_begin = translation.y; + scrollbar_end = scrollbar_begin + scale.y; + scrollbar_size = scrollbar_end - scrollbar_begin; + scrollbar_granularity = std::min(1.0f, scrollbar_size / std::max(1.0f, list_length)); + scrollbar_length = std::max(scale.x * 2, scrollbar_size * scrollbar_granularity); + } + else + { + scrollbar_begin = translation.x; + scrollbar_end = scrollbar_begin + scale.x; + scrollbar_size = scrollbar_end - scrollbar_begin; + scrollbar_granularity = std::min(1.0f, scrollbar_size / std::max(1.0f, list_length)); + scrollbar_length = std::max(scale.y * 2, scrollbar_size * scrollbar_granularity); + } + + if (!click_down) + { + scrollbar_state = SCROLLBAR_INACTIVE; + } + + if (IsScrollbarRequired() && hitbox.intersects(pointerHitbox)) + { + if (clicked) + { + scrollbar_state = SCROLLBAR_GRABBED; + grab_pos = pointerHitbox.pos; + grab_pos.x = wi::math::Clamp(grab_pos.x, scrollbar_begin + scrollbar_delta, scrollbar_begin + scrollbar_delta + scrollbar_length); + grab_pos.y = wi::math::Clamp(grab_pos.y, scrollbar_begin + scrollbar_delta, scrollbar_begin + scrollbar_delta + scrollbar_length); + grab_delta = scrollbar_delta; + } + else if (!click_down) + { + scrollbar_state = SCROLLBAR_HOVER; + state = FOCUS; + } + } + + if (scrollbar_state == SCROLLBAR_GRABBED) + { + Activate(); + if (vertical) + { + scrollbar_delta = grab_delta + pointerHitbox.pos.y - grab_pos.y; + } + else + { + scrollbar_delta = grab_delta + pointerHitbox.pos.x - grab_pos.x; + } + } + + scrollbar_delta = wi::math::Clamp(scrollbar_delta, 0, scrollbar_size - scrollbar_length); + if (scrollbar_begin < scrollbar_end - scrollbar_length) + { + scrollbar_value = wi::math::InverseLerp(scrollbar_begin, scrollbar_end - scrollbar_length, scrollbar_begin + scrollbar_delta); + } + else + { + scrollbar_value = 0; + } + + list_offset = -scrollbar_value * (list_length - scrollbar_size * (1.0f - overscroll)); + } + + if (vertical) + { + for (int i = 0; i < arraysize(sprites_knob); ++i) + { + sprites_knob[i].params.pos.x = translation.x + knob_inset_border.x; + sprites_knob[i].params.pos.y = translation.y + knob_inset_border.y + scrollbar_delta; + sprites_knob[i].params.siz.x = scale.x - knob_inset_border.x * 2; + sprites_knob[i].params.siz.y = scrollbar_length - knob_inset_border.y * 2; + } + } + else + { + for (int i = 0; i < arraysize(sprites_knob); ++i) + { + sprites_knob[i].params.pos.x = translation.x + knob_inset_border.x + scrollbar_delta; + sprites_knob[i].params.pos.y = translation.y + knob_inset_border.y; + sprites_knob[i].params.siz.x = scrollbar_length - knob_inset_border.x * 2; + sprites_knob[i].params.siz.y = scale.y - knob_inset_border.y * 2; + } + } + } + void ScrollBar::Render(const wi::Canvas& canvas, CommandList cmd) const + { + if (!IsVisible()) + { + return; + } + if (!IsScrollbarRequired()) + return; + + // scrollbar background + wi::image::Params fx = sprites[state].params; + fx.pos = XMFLOAT3(translation.x, translation.y, 0); + fx.siz = XMFLOAT2(scale.x, scale.y); + fx.color = sprites[IDLE].params.color; + wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); + + // scrollbar knob + sprites_knob[scrollbar_state].Draw(cmd); + + } + + + + void Label::Create(const std::string& name) { SetName(name); SetText(name); SetSize(XMFLOAT2(100, 20)); + + scrollbar.SetColor(wi::Color(80, 80, 80, 100), wi::gui::IDLE); + scrollbar.sprites_knob[ScrollBar::SCROLLBAR_INACTIVE].params.color = wi::Color(140, 140, 140, 140); + scrollbar.sprites_knob[ScrollBar::SCROLLBAR_HOVER].params.color = wi::Color(180, 180, 180, 180); + scrollbar.sprites_knob[ScrollBar::SCROLLBAR_GRABBED].params.color = wi::Color::White(); + scrollbar.SetOverScroll(0.25f); + scrollbar.knob_inset_border = XMFLOAT2(4, 2); } void Label::Update(const wi::Canvas& canvas, float dt) { @@ -752,6 +931,31 @@ namespace wi::gui font.params.posY = translation.y + scale.y * 0.5f; break; } + + scrollbar.SetListLength(font.TextHeight()); + scrollbar.ClearTransform(); + scrollbar.SetPos(XMFLOAT2(translation.x + scale.x - scrollbar_width, translation.y)); + scrollbar.SetSize(XMFLOAT2(scrollbar_width, scale.y)); + scrollbar.Update(canvas, dt); + + Hitbox2D pointerHitbox = GetPointerHitbox(); + if (pointerHitbox.intersects(hitBox) && !force_disable) + { + state = FOCUS; + // This is outside scrollbar code, because it can also be scrolled if parent widget is only in focus + scrollbar.Scroll(wi::input::GetPointer().z * 20); + } + else + { + state = IDLE; + } + + if (scrollbar.IsScrollbarRequired()) + { + font.params.h_wrap = scale.x - scrollbar_width; + } + + font.params.posY += scrollbar.GetOffset(); } void Label::Render(const wi::Canvas& canvas, CommandList cmd) const { @@ -766,6 +970,8 @@ namespace wi::gui sprites[state].Draw(cmd); font.Draw(cmd); + + scrollbar.Render(canvas, cmd); } @@ -1717,179 +1923,6 @@ namespace wi::gui - void ScrollBar::Update(const wi::Canvas& canvas, float dt) - { - if (!IsVisible()) - { - return; - } - - Widget::Update(canvas, dt); - - if (scale.x > scale.y) - { - vertical = false; - } - else - { - vertical = true; - } - - if (IsEnabled()) - { - if (state == FOCUS) - { - state = IDLE; - } - if (state == DEACTIVATING) - { - state = IDLE; - } - if (state == ACTIVE) - { - Deactivate(); - } - - Hitbox2D hitbox = Hitbox2D(XMFLOAT2(translation.x, translation.y), XMFLOAT2(scale.x, scale.y)); - Hitbox2D pointerHitbox = GetPointerHitbox(); - - if (state == IDLE && hitbox.intersects(pointerHitbox)) - { - state = FOCUS; - } - - bool clicked = false; - if (wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)) - { - clicked = true; - } - - bool click_down = false; - if (wi::input::Down(wi::input::MOUSE_BUTTON_LEFT)) - { - click_down = true; - if (state == FOCUS || state == DEACTIVATING) - { - // Keep pressed until mouse is released - Activate(); - } - } - float scrollbar_begin; - float scrollbar_end; - float scrollbar_size; - - if (vertical) - { - scrollbar_begin = translation.y; - scrollbar_end = scrollbar_begin + scale.y; - scrollbar_size = scrollbar_end - scrollbar_begin; - scrollbar_granularity = std::min(1.0f, scrollbar_size / std::max(1.0f, list_length)); - scrollbar_length = std::max(scale.x * 2, scrollbar_size * scrollbar_granularity); - } - else - { - scrollbar_begin = translation.x; - scrollbar_end = scrollbar_begin + scale.x; - scrollbar_size = scrollbar_end - scrollbar_begin; - scrollbar_granularity = std::min(1.0f, scrollbar_size / std::max(1.0f, list_length)); - scrollbar_length = std::max(scale.y * 2, scrollbar_size * scrollbar_granularity); - } - - if (!click_down) - { - scrollbar_state = SCROLLBAR_INACTIVE; - } - - if (IsScrollbarRequired() && hitbox.intersects(pointerHitbox)) - { - if (clicked) - { - scrollbar_state = SCROLLBAR_GRABBED; - grab_pos = pointerHitbox.pos; - grab_pos.x = wi::math::Clamp(grab_pos.x, scrollbar_begin + scrollbar_delta, scrollbar_begin + scrollbar_delta + scrollbar_length); - grab_pos.y = wi::math::Clamp(grab_pos.y, scrollbar_begin + scrollbar_delta, scrollbar_begin + scrollbar_delta + scrollbar_length); - grab_delta = scrollbar_delta; - } - else if (!click_down) - { - scrollbar_state = SCROLLBAR_HOVER; - state = FOCUS; - } - } - - if (scrollbar_state == SCROLLBAR_GRABBED) - { - Activate(); - if (vertical) - { - scrollbar_delta = grab_delta + pointerHitbox.pos.y - grab_pos.y; - } - else - { - scrollbar_delta = grab_delta + pointerHitbox.pos.x - grab_pos.x; - } - } - - scrollbar_delta = wi::math::Clamp(scrollbar_delta, 0, scrollbar_size - scrollbar_length); - if (scrollbar_begin < scrollbar_end - scrollbar_length) - { - scrollbar_value = wi::math::InverseLerp(scrollbar_begin, scrollbar_end - scrollbar_length, scrollbar_begin + scrollbar_delta); - } - else - { - scrollbar_value = 0; - } - - list_offset = -scrollbar_value * (list_length - scrollbar_size * (1.0f - overscroll)); - } - - if (vertical) - { - for (int i = 0; i < arraysize(sprites_knob); ++i) - { - sprites_knob[i].params.pos.x = translation.x + knob_inset_border.x; - sprites_knob[i].params.pos.y = translation.y + knob_inset_border.y + scrollbar_delta; - sprites_knob[i].params.siz.x = scale.x - knob_inset_border.x * 2; - sprites_knob[i].params.siz.y = scrollbar_length - knob_inset_border.y * 2; - } - } - else - { - for (int i = 0; i < arraysize(sprites_knob); ++i) - { - sprites_knob[i].params.pos.x = translation.x + knob_inset_border.x + scrollbar_delta; - sprites_knob[i].params.pos.y = translation.y + knob_inset_border.y; - sprites_knob[i].params.siz.x = scrollbar_length - knob_inset_border.x * 2; - sprites_knob[i].params.siz.y = scale.y - knob_inset_border.y * 2; - } - } - } - void ScrollBar::Render(const wi::Canvas& canvas, CommandList cmd) const - { - if (!IsVisible()) - { - return; - } - if (!IsScrollbarRequired()) - return; - - // scrollbar background - wi::image::Params fx = sprites[state].params; - fx.pos = XMFLOAT3(translation.x, translation.y, 0); - fx.siz = XMFLOAT2(scale.x, scale.y); - fx.color = sprites[IDLE].params.color; - wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); - - // scrollbar knob - sprites_knob[scrollbar_state].Draw(cmd); - - } - - - - - - static const float windowcontrolSize = 20.0f; void Window::Create(const std::string& name, bool window_controls) { diff --git a/WickedEngine/wiGUI.h b/WickedEngine/wiGUI.h index d09e904c5..d67967927 100644 --- a/WickedEngine/wiGUI.h +++ b/WickedEngine/wiGUI.h @@ -152,10 +152,56 @@ namespace wi::gui void OnDragEnd(std::function func); }; + // Generic scroll bar + class ScrollBar : public Widget + { + protected: + float scrollbar_delta = 0; + float scrollbar_length = 0; + float scrollbar_value = 0; + float scrollbar_granularity = 1; + float list_length = 0; + float list_offset = 0; + float overscroll = 0; + bool vertical = true; + XMFLOAT2 grab_pos = {}; + float grab_delta = 0; + + public: + // Set the list's length that will be scrollable and moving + void SetListLength(float size) { list_length = size; } + // The scrolling offset that should be applied to the list items + float GetOffset() const { return list_offset; } + // This can be called by user for extra scrolling on top of base functionality + void Scroll(float amount) { scrollbar_delta -= amount; } + // How much the max scrolling will offset the list even further than it would be necessary for fitting + // this value is in percent of a full scrollbar's worth of extra offset + // 0: no extra offset + // 1: full extra offset + void SetOverScroll(float amount) { overscroll = amount; } + // Check whether the scrollbar is required (when the items don't and scrolling could be used) + bool IsScrollbarRequired() const { return scrollbar_granularity < 1; } + + enum SCROLLBAR_STATE + { + SCROLLBAR_INACTIVE, + SCROLLBAR_HOVER, + SCROLLBAR_GRABBED, + SCROLLBAR_STATE_COUNT, + } scrollbar_state = SCROLLBAR_INACTIVE; + wi::Sprite sprites_knob[SCROLLBAR_STATE_COUNT]; + XMFLOAT2 knob_inset_border = {}; + + void Update(const wi::Canvas& canvas, float dt) override; + void Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const override; + }; + // Static box that holds text class Label : public Widget { protected: + ScrollBar scrollbar; + float scrollbar_width = 18; public: void Create(const std::string& name); @@ -297,50 +343,6 @@ namespace wi::gui void OnSelect(std::function func); }; - // Generic scroll bar - class ScrollBar : public Widget - { - protected: - float scrollbar_delta = 0; - float scrollbar_length = 0; - float scrollbar_value = 0; - float scrollbar_granularity = 1; - float list_length = 0; - float list_offset = 0; - float overscroll = 0; - bool vertical = true; - XMFLOAT2 grab_pos = {}; - float grab_delta = 0; - - public: - // Set the list's length that will be scrollable and moving - void SetListLength(float size) { list_length = size; } - // The scrolling offset that should be applied to the list items - float GetOffset() const { return list_offset; } - // This can be called by user for extra scrolling on top of base functionality - void Scroll(float amount) { scrollbar_delta -= amount; } - // How much the max scrolling will offset the list even further than it would be necessary for fitting - // this value is in percent of a full scrollbar's worth of extra offset - // 0: no extra offset - // 1: full extra offset - void SetOverScroll(float amount) { overscroll = amount; } - // Check whether the scrollbar is required (when the items don't and scrolling could be used) - bool IsScrollbarRequired() const { return scrollbar_granularity < 1; } - - enum SCROLLBAR_STATE - { - SCROLLBAR_INACTIVE, - SCROLLBAR_HOVER, - SCROLLBAR_GRABBED, - SCROLLBAR_STATE_COUNT, - } scrollbar_state = SCROLLBAR_INACTIVE; - wi::Sprite sprites_knob[SCROLLBAR_STATE_COUNT]; - XMFLOAT2 knob_inset_border = {}; - - void Update(const wi::Canvas& canvas, float dt) override; - void Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const override; - }; - // Widget container class Window :public Widget { diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 0d8220338..4e34ba5a9 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 60; // minor bug fixes, alterations, refactors, updates - const int revision = 70; + const int revision = 71; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);