font renderer: word wrap is fix, gui: label scrollbar

This commit is contained in:
Turánszki János
2022-05-20 15:09:57 +02:00
parent e839c235f0
commit 628bd67ea9
5 changed files with 357 additions and 348 deletions
+1 -1
View File
@@ -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);
}
+103 -129
View File
@@ -98,40 +98,46 @@ namespace wi::font
};
static wi::vector<FontStyle> fontStyles;
template<typename T>
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<FontVertex> vertexList;
template<typename T>
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<typename T>
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<typename T>
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<typename T>
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<FontVertex> 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;
}
}
+206 -173
View File
@@ -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)
{
+46 -44
View File
@@ -152,10 +152,56 @@ namespace wi::gui
void OnDragEnd(std::function<void(EventArgs args)> 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<void(EventArgs args)> 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
{
+1 -1
View File
@@ -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);