1614 lines
39 KiB
C++
1614 lines
39 KiB
C++
#include "wiWidget.h"
|
|
#include "wiGUI.h"
|
|
#include "wiImage.h"
|
|
#include "wiTextureHelper.h"
|
|
#include "wiFont.h"
|
|
#include "wiMath.h"
|
|
#include "wiHelper.h"
|
|
#include "wiInputManager.h"
|
|
|
|
#include <DirectXCollision.h>
|
|
|
|
using namespace std;
|
|
using namespace wiGraphicsTypes;
|
|
|
|
|
|
wiWidget::wiWidget():Transform()
|
|
{
|
|
state = IDLE;
|
|
enabled = true;
|
|
visible = true;
|
|
colors[IDLE] = wiColor::Booger;
|
|
colors[FOCUS] = wiColor::Gray;
|
|
colors[ACTIVE] = wiColor::White;
|
|
colors[DEACTIVATING] = wiColor::Gray;
|
|
scissorRect.bottom = 0;
|
|
scissorRect.left = 0;
|
|
scissorRect.right = 0;
|
|
scissorRect.top = 0;
|
|
container = nullptr;
|
|
tooltipTimer = 0;
|
|
textColor = wiColor(255, 255, 255, 255);
|
|
textShadowColor = wiColor(0, 0, 0, 255);
|
|
}
|
|
wiWidget::~wiWidget()
|
|
{
|
|
}
|
|
void wiWidget::Update(wiGUI* gui, float dt )
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (GetState() == WIDGETSTATE::FOCUS && !tooltip.empty())
|
|
{
|
|
tooltipTimer++;
|
|
}
|
|
else
|
|
{
|
|
tooltipTimer = 0;
|
|
}
|
|
|
|
// Only do the updatetransform if it has no parent because if it has, its transform
|
|
// will be updated down the chain anyway
|
|
if (Transform::parent == nullptr)
|
|
{
|
|
Transform::UpdateTransform();
|
|
}
|
|
}
|
|
void wiWidget::RenderTooltip(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (tooltipTimer > 25)
|
|
{
|
|
XMFLOAT2 tooltipPos = XMFLOAT2(gui->pointerpos.x, gui->pointerpos.y);
|
|
if (tooltipPos.y > wiRenderer::GetDevice()->GetScreenHeight()*0.8f)
|
|
{
|
|
tooltipPos.y -= 30;
|
|
}
|
|
else
|
|
{
|
|
tooltipPos.y += 40;
|
|
}
|
|
wiFontProps fontProps = wiFontProps((int)tooltipPos.x, (int)tooltipPos.y, -1, WIFALIGN_LEFT, WIFALIGN_TOP);
|
|
fontProps.color = wiColor(25, 25, 25, 255);
|
|
wiFont tooltipFont = wiFont(tooltip, fontProps);
|
|
if (!scriptTip.empty())
|
|
{
|
|
tooltipFont.SetText(tooltip + "\n" + scriptTip);
|
|
}
|
|
static const float _border = 2;
|
|
float fontWidth = (float)tooltipFont.textWidth() + _border* 2;
|
|
float fontHeight = (float)tooltipFont.textHeight() + _border * 2;
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(wiColor(255, 234, 165)), wiImageEffects(tooltipPos.x - _border, tooltipPos.y - _border, fontWidth, fontHeight), gui->GetGraphicsThread());
|
|
tooltipFont.SetText(tooltip);
|
|
tooltipFont.Draw(gui->GetGraphicsThread());
|
|
if (!scriptTip.empty())
|
|
{
|
|
tooltipFont.SetText(scriptTip);
|
|
tooltipFont.props.posY += (int)(fontHeight / 2);
|
|
tooltipFont.props.color = wiColor(25, 25, 25, 110);
|
|
tooltipFont.Draw(gui->GetGraphicsThread());
|
|
}
|
|
}
|
|
}
|
|
wiHashString wiWidget::GetName()
|
|
{
|
|
return fastName;
|
|
}
|
|
void wiWidget::SetName(const std::string& value)
|
|
{
|
|
name = value;
|
|
if (value.length() <= 0)
|
|
{
|
|
static unsigned long widgetID = 0;
|
|
stringstream ss("");
|
|
ss << "widget_" << widgetID;
|
|
name = ss.str();
|
|
widgetID++;
|
|
}
|
|
|
|
fastName = wiHashString(name);
|
|
}
|
|
string wiWidget::GetText()
|
|
{
|
|
return name;
|
|
}
|
|
void wiWidget::SetText(const std::string& value)
|
|
{
|
|
text = value;
|
|
}
|
|
void wiWidget::SetTooltip(const std::string& value)
|
|
{
|
|
tooltip = value;
|
|
}
|
|
void wiWidget::SetScriptTip(const std::string& value)
|
|
{
|
|
scriptTip = value;
|
|
}
|
|
void wiWidget::SetPos(const XMFLOAT2& value)
|
|
{
|
|
Transform::translation_rest.x = value.x;
|
|
Transform::translation_rest.y = value.y;
|
|
Transform::UpdateTransform();
|
|
}
|
|
void wiWidget::SetSize(const XMFLOAT2& value)
|
|
{
|
|
Transform::scale_rest.x = value.x;
|
|
Transform::scale_rest.y = value.y;
|
|
Transform::UpdateTransform();
|
|
}
|
|
wiWidget::WIDGETSTATE wiWidget::GetState()
|
|
{
|
|
return state;
|
|
}
|
|
void wiWidget::SetEnabled(bool val)
|
|
{
|
|
enabled = val;
|
|
}
|
|
bool wiWidget::IsEnabled()
|
|
{
|
|
return enabled && visible;
|
|
}
|
|
void wiWidget::SetVisible(bool val)
|
|
{
|
|
visible = val;
|
|
}
|
|
bool wiWidget::IsVisible()
|
|
{
|
|
return visible;
|
|
}
|
|
void wiWidget::Activate()
|
|
{
|
|
state = ACTIVE;
|
|
}
|
|
void wiWidget::Deactivate()
|
|
{
|
|
state = DEACTIVATING;
|
|
}
|
|
void wiWidget::SetColor(const wiColor& color, WIDGETSTATE state)
|
|
{
|
|
if (state == WIDGETSTATE_COUNT)
|
|
{
|
|
for (int i = 0; i < WIDGETSTATE_COUNT; ++i)
|
|
{
|
|
colors[i] = color;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
colors[state] = color;
|
|
}
|
|
}
|
|
wiColor wiWidget::GetColor()
|
|
{
|
|
wiColor retVal = colors[GetState()];
|
|
if (!IsEnabled()) {
|
|
retVal = wiColor::lerp(wiColor::Transparent, retVal, 0.5f);
|
|
}
|
|
return retVal;
|
|
}
|
|
void wiWidget::SetScissorRect(const wiGraphicsTypes::Rect& rect)
|
|
{
|
|
scissorRect = rect;
|
|
if(scissorRect.bottom>0)
|
|
scissorRect.bottom -= 1;
|
|
if (scissorRect.left>0)
|
|
scissorRect.left += 1;
|
|
if (scissorRect.right>0)
|
|
scissorRect.right -= 1;
|
|
if (scissorRect.top>0)
|
|
scissorRect.top += 1;
|
|
}
|
|
|
|
|
|
wiButton::wiButton(const std::string& name) :wiWidget()
|
|
{
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
OnClick([](wiEventArgs args) {});
|
|
OnDragStart([](wiEventArgs args) {});
|
|
OnDrag([](wiEventArgs args) {});
|
|
OnDragEnd([](wiEventArgs args) {});
|
|
SetSize(XMFLOAT2(100, 30));
|
|
}
|
|
wiButton::~wiButton()
|
|
{
|
|
|
|
}
|
|
void wiButton::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
hitBox.pos.x = Transform::translation.x;
|
|
hitBox.pos.y = Transform::translation.y;
|
|
hitBox.siz.x = Transform::scale.x;
|
|
hitBox.siz.y = Transform::scale.y;
|
|
|
|
Hitbox2D pointerHitbox = Hitbox2D(gui->GetPointerPos(), XMFLOAT2(1, 1));
|
|
|
|
if (state == FOCUS)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == DEACTIVATING)
|
|
{
|
|
wiEventArgs args;
|
|
args.clickPos = pointerHitbox.pos;
|
|
onDragEnd(args);
|
|
|
|
if (pointerHitbox.intersects(hitBox))
|
|
{
|
|
// Click occurs when the button is released within the bounds
|
|
onClick(args);
|
|
}
|
|
|
|
state = IDLE;
|
|
}
|
|
if (state == ACTIVE)
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
}
|
|
|
|
bool clicked = false;
|
|
// hover the button
|
|
if (pointerHitbox.intersects(hitBox))
|
|
{
|
|
if (state == IDLE)
|
|
{
|
|
state = FOCUS;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->press(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == FOCUS)
|
|
{
|
|
// activate
|
|
clicked = true;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->down(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == DEACTIVATING)
|
|
{
|
|
// Keep pressed until mouse is released
|
|
gui->ActivateWidget(this);
|
|
|
|
wiEventArgs args;
|
|
args.clickPos = pointerHitbox.pos;
|
|
XMFLOAT3 posDelta;
|
|
posDelta.x = pointerHitbox.pos.x - prevPos.x;
|
|
posDelta.y = pointerHitbox.pos.y - prevPos.y;
|
|
posDelta.z = 0;
|
|
args.deltaPos = XMFLOAT2(posDelta.x, posDelta.y);
|
|
onDrag(args);
|
|
}
|
|
}
|
|
|
|
if (clicked)
|
|
{
|
|
wiEventArgs args;
|
|
args.clickPos = pointerHitbox.pos;
|
|
dragStart = args.clickPos;
|
|
args.startPos = dragStart;
|
|
onDragStart(args);
|
|
gui->ActivateWidget(this);
|
|
}
|
|
|
|
prevPos.x = pointerHitbox.pos.x;
|
|
prevPos.y = pointerHitbox.pos.y;
|
|
|
|
}
|
|
void wiButton::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x, translation.y, scale.x, scale.y), gui->GetGraphicsThread());
|
|
|
|
|
|
|
|
scissorRect.bottom = (LONG)(translation.y + scale.y);
|
|
scissorRect.left = (LONG)(translation.x);
|
|
scissorRect.right = (LONG)(translation.x + scale.x);
|
|
scissorRect.top = (LONG)(translation.y);
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
wiFont(text, wiFontProps((int)(translation.x + scale.x*0.5f), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_CENTER, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), true);
|
|
|
|
}
|
|
void wiButton::OnClick(function<void(wiEventArgs args)> func)
|
|
{
|
|
onClick = move(func);
|
|
}
|
|
void wiButton::OnDragStart(function<void(wiEventArgs args)> func)
|
|
{
|
|
onDragStart = move(func);
|
|
}
|
|
void wiButton::OnDrag(function<void(wiEventArgs args)> func)
|
|
{
|
|
onDrag = move(func);
|
|
}
|
|
void wiButton::OnDragEnd(function<void(wiEventArgs args)> func)
|
|
{
|
|
onDragEnd = move(func);
|
|
}
|
|
|
|
|
|
|
|
|
|
wiLabel::wiLabel(const std::string& name) :wiWidget()
|
|
{
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
SetSize(XMFLOAT2(100, 20));
|
|
}
|
|
wiLabel::~wiLabel()
|
|
{
|
|
|
|
}
|
|
void wiLabel::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
void wiLabel::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x, translation.y, scale.x, scale.y), gui->GetGraphicsThread());
|
|
|
|
|
|
scissorRect.bottom = (LONG)(translation.y + scale.y);
|
|
scissorRect.left = (LONG)(translation.x);
|
|
scissorRect.right = (LONG)(translation.x + scale.x);
|
|
scissorRect.top = (LONG)(translation.y);
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
wiFont(text, wiFontProps((int)translation.x, (int)translation.y, -1, WIFALIGN_LEFT, WIFALIGN_TOP, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wiSlider::wiSlider(float start, float end, float defaultValue, float step, const std::string& name) :wiWidget()
|
|
,start(start), end(end), value(defaultValue), step(max(step, 1))
|
|
{
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
OnSlide([](wiEventArgs args) {});
|
|
SetSize(XMFLOAT2(200, 40));
|
|
}
|
|
wiSlider::~wiSlider()
|
|
{
|
|
}
|
|
void wiSlider::SetValue(float value)
|
|
{
|
|
this->value = value;
|
|
}
|
|
float wiSlider::GetValue()
|
|
{
|
|
return value;
|
|
}
|
|
void wiSlider::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool dragged = false;
|
|
|
|
if (state == FOCUS)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == DEACTIVATING)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == ACTIVE)
|
|
{
|
|
if (wiInputManager::GetInstance()->down(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == ACTIVE)
|
|
{
|
|
// continue drag if already grabbed wheter it is intersecting or not
|
|
dragged = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
}
|
|
}
|
|
|
|
float headWidth = scale.x*0.05f;
|
|
|
|
hitBox.pos.x = Transform::translation.x - headWidth*0.5f;
|
|
hitBox.pos.y = Transform::translation.y;
|
|
hitBox.siz.x = Transform::scale.x + headWidth;
|
|
hitBox.siz.y = Transform::scale.y;
|
|
|
|
Hitbox2D pointerHitbox = Hitbox2D(gui->GetPointerPos(), XMFLOAT2(1, 1));
|
|
|
|
|
|
if (pointerHitbox.intersects(hitBox))
|
|
{
|
|
// hover the slider
|
|
if (state == IDLE)
|
|
{
|
|
state = FOCUS;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->press(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == FOCUS)
|
|
{
|
|
// activate
|
|
dragged = true;
|
|
}
|
|
}
|
|
|
|
|
|
if (dragged)
|
|
{
|
|
wiEventArgs args;
|
|
args.clickPos = pointerHitbox.pos;
|
|
value = wiMath::InverseLerp(translation.x, translation.x + scale.x, args.clickPos.x);
|
|
value = wiMath::Clamp(value, 0, 1);
|
|
value *= step;
|
|
value = floorf(value);
|
|
value /= step;
|
|
value = wiMath::Lerp(start, end, value);
|
|
args.fValue = value;
|
|
args.iValue = (int)value;
|
|
onSlide(args);
|
|
gui->ActivateWidget(this);
|
|
}
|
|
|
|
}
|
|
void wiSlider::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
|
|
float headWidth = scale.x*0.05f;
|
|
|
|
// trail
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x - headWidth*0.5f, translation.y + scale.y * 0.5f - scale.y*0.1f, scale.x + headWidth, scale.y * 0.2f), gui->GetGraphicsThread());
|
|
// head
|
|
float headPosX = wiMath::Lerp(translation.x, translation.x + scale.x, wiMath::Clamp(wiMath::InverseLerp(start, end, value), 0, 1));
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(headPosX - headWidth * 0.5f, translation.y, headWidth, scale.y), gui->GetGraphicsThread());
|
|
|
|
if (parent != nullptr)
|
|
{
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
}
|
|
// text
|
|
wiFont(text, wiFontProps((int)(translation.x - headWidth * 0.5f), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_RIGHT, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor )).Draw(gui->GetGraphicsThread(), parent != nullptr);
|
|
// value
|
|
stringstream ss("");
|
|
ss << value;
|
|
wiFont(ss.str(), wiFontProps((int)(translation.x + scale.x + headWidth), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_LEFT, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor )).Draw(gui->GetGraphicsThread(), parent != nullptr);
|
|
|
|
|
|
}
|
|
void wiSlider::OnSlide(function<void(wiEventArgs args)> func)
|
|
{
|
|
onSlide = move(func);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wiCheckBox::wiCheckBox(const std::string& name) :wiWidget()
|
|
,checked(false)
|
|
{
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
OnClick([](wiEventArgs args) {});
|
|
SetSize(XMFLOAT2(20,20));
|
|
}
|
|
wiCheckBox::~wiCheckBox()
|
|
{
|
|
|
|
}
|
|
void wiCheckBox::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (state == FOCUS)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == DEACTIVATING)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == ACTIVE)
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
}
|
|
|
|
hitBox.pos.x = Transform::translation.x;
|
|
hitBox.pos.y = Transform::translation.y;
|
|
hitBox.siz.x = Transform::scale.x;
|
|
hitBox.siz.y = Transform::scale.y;
|
|
|
|
Hitbox2D pointerHitbox = Hitbox2D(gui->GetPointerPos(), XMFLOAT2(1, 1));
|
|
|
|
bool clicked = false;
|
|
// hover the button
|
|
if (pointerHitbox.intersects(hitBox))
|
|
{
|
|
if (state == IDLE)
|
|
{
|
|
state = FOCUS;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->press(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == FOCUS)
|
|
{
|
|
// activate
|
|
clicked = true;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->down(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == DEACTIVATING)
|
|
{
|
|
// Keep pressed until mouse is released
|
|
gui->ActivateWidget(this);
|
|
}
|
|
}
|
|
|
|
if (clicked)
|
|
{
|
|
SetCheck(!GetCheck());
|
|
wiEventArgs args;
|
|
args.clickPos = pointerHitbox.pos;
|
|
args.bValue = GetCheck();
|
|
onClick(args);
|
|
gui->ActivateWidget(this);
|
|
}
|
|
|
|
}
|
|
void wiCheckBox::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
|
|
// control
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x, translation.y, scale.x, scale.y), gui->GetGraphicsThread());
|
|
|
|
// check
|
|
if (GetCheck())
|
|
{
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(wiColor::lerp(color, wiColor::White, 0.8f))
|
|
, wiImageEffects(translation.x + scale.x*0.25f, translation.y + scale.y*0.25f, scale.x*0.5f, scale.y*0.5f)
|
|
, gui->GetGraphicsThread());
|
|
}
|
|
|
|
if (parent != nullptr)
|
|
{
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
}
|
|
wiFont(text, wiFontProps((int)(translation.x), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_RIGHT, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor )).Draw(gui->GetGraphicsThread(), parent != nullptr);
|
|
|
|
}
|
|
void wiCheckBox::OnClick(function<void(wiEventArgs args)> func)
|
|
{
|
|
onClick = move(func);
|
|
}
|
|
void wiCheckBox::SetCheck(bool value)
|
|
{
|
|
checked = value;
|
|
}
|
|
bool wiCheckBox::GetCheck()
|
|
{
|
|
return checked;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wiComboBox::wiComboBox(const std::string& name) :wiWidget()
|
|
, selected(-1), combostate(COMBOSTATE_INACTIVE), hovered(-1)
|
|
{
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
OnSelect([](wiEventArgs args) {});
|
|
SetSize(XMFLOAT2(100, 20));
|
|
SetMaxVisibleItemCount(8);
|
|
firstItemVisible = 0;
|
|
}
|
|
wiComboBox::~wiComboBox()
|
|
{
|
|
|
|
}
|
|
const float wiComboBox::_GetItemOffset(int index) const
|
|
{
|
|
index = max(firstItemVisible, index) - firstItemVisible;
|
|
return scale.y * (index + 1) + 1;
|
|
}
|
|
void wiComboBox::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (state == FOCUS)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == DEACTIVATING)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
if (state == ACTIVE && combostate == COMBOSTATE_SELECTING)
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
}
|
|
|
|
hitBox.pos.x = Transform::translation.x;
|
|
hitBox.pos.y = Transform::translation.y;
|
|
hitBox.siz.x = Transform::scale.x + scale.y + 1; // + drop-down indicator arrow + little offset
|
|
hitBox.siz.y = Transform::scale.y;
|
|
|
|
Hitbox2D pointerHitbox = Hitbox2D(gui->GetPointerPos(), XMFLOAT2(1, 1));
|
|
|
|
bool clicked = false;
|
|
// hover the button
|
|
if (pointerHitbox.intersects(hitBox))
|
|
{
|
|
if (state == IDLE)
|
|
{
|
|
state = FOCUS;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->press(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
// activate
|
|
clicked = true;
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->down(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == DEACTIVATING)
|
|
{
|
|
// Keep pressed until mouse is released
|
|
gui->ActivateWidget(this);
|
|
}
|
|
}
|
|
|
|
|
|
if (clicked && state == FOCUS)
|
|
{
|
|
gui->ActivateWidget(this);
|
|
}
|
|
|
|
|
|
if (state == ACTIVE)
|
|
{
|
|
if (combostate == COMBOSTATE_INACTIVE)
|
|
{
|
|
combostate = COMBOSTATE_HOVER;
|
|
}
|
|
else if (combostate == COMBOSTATE_SELECTING)
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
combostate = COMBOSTATE_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
int scroll = (int)wiInputManager::GetInstance()->getpointer().z;
|
|
firstItemVisible -= scroll;
|
|
firstItemVisible = max(0, min((int)items.size() - maxVisibleItemCount, firstItemVisible));
|
|
|
|
hovered = -1;
|
|
for (size_t i = 0; i < items.size(); ++i)
|
|
{
|
|
if ((int)i<firstItemVisible || (int)i>firstItemVisible + maxVisibleItemCount)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Hitbox2D itembox;
|
|
itembox.pos.x = Transform::translation.x;
|
|
itembox.pos.y = Transform::translation.y + _GetItemOffset((int)i);
|
|
itembox.siz.x = Transform::scale.x;
|
|
itembox.siz.y = Transform::scale.y;
|
|
if (pointerHitbox.intersects(itembox))
|
|
{
|
|
hovered = (int)i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (clicked)
|
|
{
|
|
combostate = COMBOSTATE_SELECTING;
|
|
if (hovered >= 0)
|
|
{
|
|
SetSelected(hovered);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
void wiComboBox::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
if (combostate != COMBOSTATE_INACTIVE)
|
|
{
|
|
color = colors[FOCUS];
|
|
}
|
|
|
|
// control-base
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x, translation.y, scale.x, scale.y), gui->GetGraphicsThread());
|
|
// control-arrow
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x+scale.x+1, translation.y, scale.y, scale.y), gui->GetGraphicsThread());
|
|
wiFont("V", wiFontProps((int)(translation.x+scale.x+scale.y*0.5f), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_CENTER, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), false);
|
|
|
|
|
|
if (parent != nullptr)
|
|
{
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
}
|
|
wiFont(text, wiFontProps((int)(translation.x), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_RIGHT, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), parent != nullptr);
|
|
|
|
if (selected >= 0)
|
|
{
|
|
wiFont(items[selected], wiFontProps((int)(translation.x + scale.x*0.5f), (int)(translation.y + scale.y*0.5f), -1, WIFALIGN_CENTER, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), parent != nullptr);
|
|
}
|
|
|
|
// drop-down
|
|
if (state == ACTIVE)
|
|
{
|
|
// control-list
|
|
int i = 0;
|
|
for (auto& x : items)
|
|
{
|
|
if (i<firstItemVisible || i>firstItemVisible + maxVisibleItemCount)
|
|
{
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
wiColor col = colors[IDLE];
|
|
if (hovered == i)
|
|
{
|
|
if (combostate == COMBOSTATE_HOVER)
|
|
{
|
|
col = colors[FOCUS];
|
|
}
|
|
else if (combostate == COMBOSTATE_SELECTING)
|
|
{
|
|
col = colors[ACTIVE];
|
|
}
|
|
}
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(col)
|
|
, wiImageEffects(translation.x, translation.y + _GetItemOffset(i), scale.x, scale.y), gui->GetGraphicsThread());
|
|
wiFont(x, wiFontProps((int)(translation.x + scale.x*0.5f), (int)(translation.y + scale.y*0.5f +_GetItemOffset(i)), -1, WIFALIGN_CENTER, WIFALIGN_CENTER, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), false);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
void wiComboBox::OnSelect(function<void(wiEventArgs args)> func)
|
|
{
|
|
onSelect = move(func);
|
|
}
|
|
void wiComboBox::AddItem(const std::string& item)
|
|
{
|
|
items.push_back(item);
|
|
|
|
if (selected < 0)
|
|
{
|
|
selected = 0;
|
|
}
|
|
}
|
|
void wiComboBox::RemoveItem(int index)
|
|
{
|
|
std::vector<string> newItems(0);
|
|
newItems.reserve(items.size());
|
|
for (size_t i = 0; i < items.size(); ++i)
|
|
{
|
|
if (i != index)
|
|
{
|
|
newItems.push_back(items[i]);
|
|
}
|
|
}
|
|
items = newItems;
|
|
|
|
if (items.empty())
|
|
{
|
|
selected = -1;
|
|
}
|
|
else if (selected > index)
|
|
{
|
|
selected--;
|
|
}
|
|
}
|
|
void wiComboBox::ClearItems()
|
|
{
|
|
items.clear();
|
|
|
|
selected = -1;
|
|
}
|
|
void wiComboBox::SetMaxVisibleItemCount(int value)
|
|
{
|
|
maxVisibleItemCount = value;
|
|
}
|
|
void wiComboBox::SetSelected(int index)
|
|
{
|
|
selected = index;
|
|
|
|
wiEventArgs args;
|
|
args.iValue = selected;
|
|
args.sValue = GetItemText(selected);
|
|
onSelect(args);
|
|
}
|
|
string wiComboBox::GetItemText(int index)
|
|
{
|
|
if (index >= 0)
|
|
{
|
|
return items[index];
|
|
}
|
|
return "";
|
|
}
|
|
int wiComboBox::GetSelected()
|
|
{
|
|
return selected;
|
|
}
|
|
|
|
|
|
|
|
|
|
static const float windowcontrolSize = 20.0f;
|
|
wiWindow::wiWindow(wiGUI* gui, const std::string& name) :wiWidget()
|
|
, gui(gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
SetName(name);
|
|
SetText(fastName.GetString());
|
|
SetSize(XMFLOAT2(640, 480));
|
|
|
|
// Add controls
|
|
|
|
SAFE_INIT(closeButton);
|
|
SAFE_INIT(moveDragger);
|
|
SAFE_INIT(resizeDragger_BottomRight);
|
|
SAFE_INIT(resizeDragger_UpperLeft);
|
|
|
|
|
|
|
|
// Add a grabber onto the title bar
|
|
moveDragger = new wiButton(name + "_move_dragger");
|
|
moveDragger->SetText("");
|
|
moveDragger->SetSize(XMFLOAT2(scale.x - windowcontrolSize * 3, windowcontrolSize));
|
|
moveDragger->SetPos(XMFLOAT2(windowcontrolSize, 0));
|
|
moveDragger->OnDrag([this](wiEventArgs args) {
|
|
this->Translate(XMFLOAT3(args.deltaPos.x, args.deltaPos.y, 0));
|
|
});
|
|
AddWidget(moveDragger);
|
|
|
|
// Add close button to the top right corner
|
|
closeButton = new wiButton(name + "_close_button");
|
|
closeButton->SetText("x");
|
|
closeButton->SetSize(XMFLOAT2(windowcontrolSize, windowcontrolSize));
|
|
closeButton->SetPos(XMFLOAT2(translation.x + scale.x - windowcontrolSize, translation.y));
|
|
closeButton->OnClick([this](wiEventArgs args) {
|
|
this->SetVisible(false);
|
|
});
|
|
closeButton->SetTooltip("Close window");
|
|
AddWidget(closeButton);
|
|
|
|
// Add minimize button to the top right corner
|
|
minimizeButton = new wiButton(name + "_minimize_button");
|
|
minimizeButton->SetText("-");
|
|
minimizeButton->SetSize(XMFLOAT2(windowcontrolSize, windowcontrolSize));
|
|
minimizeButton->SetPos(XMFLOAT2(translation.x + scale.x - windowcontrolSize *2, translation.y));
|
|
minimizeButton->OnClick([this](wiEventArgs args) {
|
|
this->SetMinimized(!this->IsMinimized());
|
|
});
|
|
minimizeButton->SetTooltip("Minimize window");
|
|
AddWidget(minimizeButton);
|
|
|
|
// Add a resizer control to the upperleft corner
|
|
resizeDragger_UpperLeft = new wiButton(name + "_resize_dragger_upper_left");
|
|
resizeDragger_UpperLeft->SetText("");
|
|
resizeDragger_UpperLeft->SetSize(XMFLOAT2(windowcontrolSize, windowcontrolSize));
|
|
resizeDragger_UpperLeft->SetPos(XMFLOAT2(0, 0));
|
|
resizeDragger_UpperLeft->OnDrag([this](wiEventArgs args) {
|
|
XMFLOAT2 scaleDiff;
|
|
scaleDiff.x = (scale.x - args.deltaPos.x) / scale.x;
|
|
scaleDiff.y = (scale.y - args.deltaPos.y) / scale.y;
|
|
this->Translate(XMFLOAT3(args.deltaPos.x, args.deltaPos.y, 0));
|
|
this->Scale(XMFLOAT3(scaleDiff.x, scaleDiff.y, 1));
|
|
});
|
|
AddWidget(resizeDragger_UpperLeft);
|
|
|
|
// Add a resizer control to the bottom right corner
|
|
resizeDragger_BottomRight = new wiButton(name + "_resize_dragger_bottom_right");
|
|
resizeDragger_BottomRight->SetText("");
|
|
resizeDragger_BottomRight->SetSize(XMFLOAT2(windowcontrolSize, windowcontrolSize));
|
|
resizeDragger_BottomRight->SetPos(XMFLOAT2(translation.x + scale.x - windowcontrolSize, translation.y + scale.y - windowcontrolSize));
|
|
resizeDragger_BottomRight->OnDrag([this](wiEventArgs args) {
|
|
XMFLOAT2 scaleDiff;
|
|
scaleDiff.x = (scale.x + args.deltaPos.x) / scale.x;
|
|
scaleDiff.y = (scale.y + args.deltaPos.y) / scale.y;
|
|
this->Scale(XMFLOAT3(scaleDiff.x, scaleDiff.y, 1));
|
|
});
|
|
AddWidget(resizeDragger_BottomRight);
|
|
|
|
|
|
SetEnabled(true);
|
|
SetVisible(true);
|
|
SetMinimized(false);
|
|
}
|
|
wiWindow::~wiWindow()
|
|
{
|
|
SAFE_DELETE(closeButton);
|
|
SAFE_DELETE(moveDragger);
|
|
SAFE_DELETE(resizeDragger_BottomRight);
|
|
SAFE_DELETE(resizeDragger_UpperLeft);
|
|
}
|
|
void wiWindow::AddWidget(wiWidget* widget)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
widget->SetEnabled(this->IsEnabled());
|
|
widget->SetVisible(this->IsVisible());
|
|
gui->AddWidget(widget);
|
|
widget->attachTo(this);
|
|
widget->container = this;
|
|
|
|
childrenWidgets.push_back(widget);
|
|
}
|
|
void wiWindow::RemoveWidget(wiWidget* widget)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
gui->RemoveWidget(widget);
|
|
widget->detach();
|
|
|
|
childrenWidgets.remove(widget);
|
|
}
|
|
void wiWindow::RemoveWidgets(bool alsoDelete)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
x->detach();
|
|
gui->RemoveWidget(x);
|
|
if (alsoDelete)
|
|
{
|
|
SAFE_DELETE(x);
|
|
}
|
|
}
|
|
|
|
childrenWidgets.clear();
|
|
}
|
|
void wiWindow::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWidget::Update(gui, dt);
|
|
|
|
//// TODO: Override window controls for nicer alignment:
|
|
//if (moveDragger != nullptr)
|
|
//{
|
|
// moveDragger->scale.y = windowcontrolSize;
|
|
//}
|
|
//if (resizeDragger_UpperLeft != nullptr)
|
|
//{
|
|
// resizeDragger_UpperLeft->scale.x = windowcontrolSize;
|
|
// resizeDragger_UpperLeft->scale.y = windowcontrolSize;
|
|
//}
|
|
//if (resizeDragger_BottomRight != nullptr)
|
|
//{
|
|
// resizeDragger_BottomRight->scale.x = windowcontrolSize;
|
|
// resizeDragger_BottomRight->scale.y = windowcontrolSize;
|
|
//}
|
|
//if (closeButton != nullptr)
|
|
//{
|
|
// closeButton->scale.x = windowcontrolSize;
|
|
// closeButton->scale.y = windowcontrolSize;
|
|
//}
|
|
//if (minimizeButton != nullptr)
|
|
//{
|
|
// minimizeButton->scale.x = windowcontrolSize;
|
|
// minimizeButton->scale.y = windowcontrolSize;
|
|
//}
|
|
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
x->Update(gui, dt);
|
|
x->SetScissorRect(scissorRect);
|
|
}
|
|
|
|
if (gui->IsWidgetDisabled(this))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
void wiWindow::Render(wiGUI* gui)
|
|
{
|
|
assert(gui != nullptr && "Ivalid GUI!");
|
|
|
|
if (!IsVisible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
wiColor color = GetColor();
|
|
|
|
// body
|
|
if (!IsMinimized())
|
|
{
|
|
wiImage::Draw(wiTextureHelper::getInstance()->getColor(color)
|
|
, wiImageEffects(translation.x, translation.y, scale.x, scale.y), gui->GetGraphicsThread());
|
|
}
|
|
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
if (x != gui->GetActiveWidget())
|
|
{
|
|
// the gui will render the active on on top of everything!
|
|
x->Render(gui);
|
|
}
|
|
}
|
|
|
|
scissorRect.bottom = (LONG)(translation.y + scale.y);
|
|
scissorRect.left = (LONG)(translation.x);
|
|
scissorRect.right = (LONG)(translation.x + scale.x);
|
|
scissorRect.top = (LONG)(translation.y);
|
|
wiRenderer::GetDevice()->SetScissorRects(1, &scissorRect, gui->GetGraphicsThread());
|
|
wiFont(text, wiFontProps((int)(translation.x + resizeDragger_UpperLeft->scale.x + 2), (int)(translation.y), -1, WIFALIGN_LEFT, WIFALIGN_TOP, 2, 1,
|
|
textColor, textShadowColor)).Draw(gui->GetGraphicsThread(), true);
|
|
|
|
}
|
|
void wiWindow::SetVisible(bool value)
|
|
{
|
|
wiWidget::SetVisible(value);
|
|
SetMinimized(!value);
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
x->SetVisible(value);
|
|
}
|
|
}
|
|
void wiWindow::SetEnabled(bool value)
|
|
{
|
|
//wiWidget::SetEnabled(value);
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
if (x == moveDragger)
|
|
continue;
|
|
if (x == minimizeButton)
|
|
continue;
|
|
if (x == closeButton)
|
|
continue;
|
|
if (x == resizeDragger_UpperLeft)
|
|
continue;
|
|
x->SetEnabled(value);
|
|
}
|
|
}
|
|
void wiWindow::SetMinimized(bool value)
|
|
{
|
|
minimized = value;
|
|
|
|
if (resizeDragger_BottomRight != nullptr)
|
|
{
|
|
resizeDragger_BottomRight->SetVisible(!value);
|
|
}
|
|
for (auto& x : childrenWidgets)
|
|
{
|
|
if (x == moveDragger)
|
|
continue;
|
|
if (x == minimizeButton)
|
|
continue;
|
|
if (x == closeButton)
|
|
continue;
|
|
if (x == resizeDragger_UpperLeft)
|
|
continue;
|
|
x->SetVisible(!value);
|
|
}
|
|
}
|
|
bool wiWindow::IsMinimized()
|
|
{
|
|
return minimized;
|
|
}
|
|
|
|
|
|
|
|
|
|
wiColorPicker::wiColorPicker(wiGUI* gui, const std::string& name) :wiWindow(gui, name)
|
|
{
|
|
SetSize(XMFLOAT2(300, 260));
|
|
SetColor(wiColor::Ghost);
|
|
RemoveWidget(resizeDragger_BottomRight);
|
|
RemoveWidget(resizeDragger_UpperLeft);
|
|
|
|
hue_picker = XMFLOAT2(0, 0);
|
|
saturation_picker = XMFLOAT2(0, 0);
|
|
saturation_picker_barycentric = XMFLOAT3(0, 1, 0);
|
|
hue_color = XMFLOAT4(1, 0, 0, 1);
|
|
final_color = XMFLOAT4(1, 1, 1, 1);
|
|
angle = 0;
|
|
}
|
|
wiColorPicker::~wiColorPicker()
|
|
{
|
|
}
|
|
static const float __colorpicker_center = 120;
|
|
static const float __colorpicker_radius_triangle = 75;
|
|
static const float __colorpicker_radius = 80;
|
|
static const float __colorpicker_width = 16;
|
|
void wiColorPicker::Update(wiGUI* gui, float dt )
|
|
{
|
|
wiWindow::Update(gui, dt);
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if (!gui->IsWidgetDisabled(this))
|
|
//{
|
|
// return;
|
|
//}
|
|
|
|
if (state == DEACTIVATING)
|
|
{
|
|
state = IDLE;
|
|
}
|
|
|
|
XMFLOAT2 center = XMFLOAT2(translation.x + __colorpicker_center, translation.y + __colorpicker_center);
|
|
XMFLOAT2 pointer = gui->GetPointerPos();
|
|
float distance = wiMath::Distance(center, pointer);
|
|
bool hover_hue = (distance > __colorpicker_radius) && (distance < __colorpicker_radius + __colorpicker_width);
|
|
|
|
float distTri = 0;
|
|
XMFLOAT4 A, B, C;
|
|
wiMath::ConstructTriangleEquilateral(__colorpicker_radius_triangle, A, B, C);
|
|
XMVECTOR _A = XMLoadFloat4(&A);
|
|
XMVECTOR _B = XMLoadFloat4(&B);
|
|
XMVECTOR _C = XMLoadFloat4(&C);
|
|
XMMATRIX _triTransform = XMMatrixRotationZ(-angle) * XMMatrixTranslation(center.x, center.y, 0);
|
|
_A = XMVector4Transform(_A, _triTransform);
|
|
_B = XMVector4Transform(_B, _triTransform);
|
|
_C = XMVector4Transform(_C, _triTransform);
|
|
XMVECTOR O = XMVectorSet(pointer.x, pointer.y, 0, 0);
|
|
XMVECTOR D = XMVectorSet(0, 0, 1, 0);
|
|
bool hover_saturation = TriangleTests::Intersects(O, D, _A, _B, _C, distTri);
|
|
|
|
if (hover_hue && state==IDLE)
|
|
{
|
|
state = FOCUS;
|
|
huefocus = true;
|
|
}
|
|
else if (hover_saturation && state == IDLE)
|
|
{
|
|
state = FOCUS;
|
|
huefocus = false;
|
|
}
|
|
else if (state == IDLE)
|
|
{
|
|
huefocus = false;
|
|
}
|
|
|
|
bool dragged = false;
|
|
|
|
if (wiInputManager::GetInstance()->press(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == FOCUS)
|
|
{
|
|
// activate
|
|
dragged = true;
|
|
}
|
|
}
|
|
|
|
if (wiInputManager::GetInstance()->down(VK_LBUTTON, wiInputManager::KEYBOARD))
|
|
{
|
|
if (state == ACTIVE)
|
|
{
|
|
// continue drag if already grabbed wheter it is intersecting or not
|
|
dragged = true;
|
|
}
|
|
}
|
|
|
|
dragged = dragged && !gui->IsWidgetDisabled(this);
|
|
if (huefocus && dragged)
|
|
{
|
|
//hue pick
|
|
angle = wiMath::GetAngle(XMFLOAT2(pointer.x - center.x, pointer.y - center.y), XMFLOAT2(__colorpicker_radius, 0));
|
|
XMFLOAT3 color3 = wiMath::HueToRGB(angle / XM_2PI);
|
|
hue_color = XMFLOAT4(color3.x, color3.y, color3.z, 1);
|
|
gui->ActivateWidget(this);
|
|
}
|
|
else if (!huefocus && dragged)
|
|
{
|
|
// saturation pick
|
|
wiMath::GetBarycentric(O, _A, _B, _C, saturation_picker_barycentric.x, saturation_picker_barycentric.y, saturation_picker_barycentric.z, true);
|
|
gui->ActivateWidget(this);
|
|
}
|
|
else if(state != IDLE)
|
|
{
|
|
gui->DeactivateWidget(this);
|
|
}
|
|
|
|
float r = __colorpicker_radius + __colorpicker_width*0.5f;
|
|
hue_picker = XMFLOAT2(center.x + r*cos(angle), center.y + r*-sin(angle));
|
|
XMStoreFloat2(&saturation_picker, _A*saturation_picker_barycentric.x + _B*saturation_picker_barycentric.y + _C*saturation_picker_barycentric.z);
|
|
XMStoreFloat4(&final_color, XMLoadFloat4(&hue_color)*saturation_picker_barycentric.x + XMVectorSet(1, 1, 1, 1)*saturation_picker_barycentric.y + XMVectorSet(0, 0, 0, 1)*saturation_picker_barycentric.z);
|
|
|
|
if (dragged)
|
|
{
|
|
wiEventArgs args;
|
|
args.clickPos = pointer;
|
|
args.fValue = angle;
|
|
args.color = final_color;
|
|
onColorChanged(args);
|
|
}
|
|
}
|
|
void wiColorPicker::Render(wiGUI* gui)
|
|
{
|
|
wiWindow::Render(gui);
|
|
|
|
|
|
if (!IsVisible() || IsMinimized())
|
|
{
|
|
return;
|
|
}
|
|
GRAPHICSTHREAD threadID = gui->GetGraphicsThread();
|
|
|
|
struct Vertex
|
|
{
|
|
XMFLOAT4 pos;
|
|
XMFLOAT4 col;
|
|
};
|
|
static wiGraphicsTypes::GPUBuffer vb_saturation;
|
|
static wiGraphicsTypes::GPUBuffer vb_hue;
|
|
static wiGraphicsTypes::GPUBuffer vb_picker;
|
|
static wiGraphicsTypes::GPUBuffer vb_preview;
|
|
|
|
static bool buffersComplete = false;
|
|
if (!buffersComplete)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
// saturation
|
|
{
|
|
static std::vector<Vertex> vertices(0);
|
|
if (vb_saturation.IsValid() && !vertices.empty())
|
|
{
|
|
vertices[0].col = hue_color;
|
|
wiRenderer::GetDevice()->UpdateBuffer(&vb_saturation, vertices.data(), threadID, vb_saturation.GetDesc().ByteWidth);
|
|
}
|
|
else
|
|
{
|
|
vertices.push_back({ XMFLOAT4(0,0,0,0),XMFLOAT4(1,0,0,1) }); // hue
|
|
vertices.push_back({ XMFLOAT4(0,0,0,0),XMFLOAT4(1,1,1,1) }); // white
|
|
vertices.push_back({ XMFLOAT4(0,0,0,0),XMFLOAT4(0,0,0,1) }); // black
|
|
wiMath::ConstructTriangleEquilateral(__colorpicker_radius_triangle, vertices[0].pos, vertices[1].pos, vertices[2].pos);
|
|
|
|
GPUBufferDesc desc;
|
|
desc.BindFlags = BIND_VERTEX_BUFFER;
|
|
desc.ByteWidth = (UINT)(vertices.size() * sizeof(Vertex));
|
|
desc.CPUAccessFlags = CPU_ACCESS_WRITE;
|
|
desc.MiscFlags = 0;
|
|
desc.StructureByteStride = 0;
|
|
desc.Usage = USAGE_DYNAMIC;
|
|
SubresourceData data;
|
|
data.pSysMem = vertices.data();
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&desc, &data, &vb_saturation);
|
|
}
|
|
}
|
|
// hue
|
|
{
|
|
std::vector<Vertex> vertices(0);
|
|
for (float i = 0; i <= 100; i += 1.0f)
|
|
{
|
|
float p = i / 100;
|
|
float t = p * XM_2PI;
|
|
float x = cos(t);
|
|
float y = -sin(t);
|
|
XMFLOAT3 rgb = wiMath::HueToRGB(p);
|
|
vertices.push_back({ XMFLOAT4(__colorpicker_radius * x, __colorpicker_radius * y, 0, 1), XMFLOAT4(rgb.x,rgb.y,rgb.z,1) });
|
|
vertices.push_back({ XMFLOAT4((__colorpicker_radius + __colorpicker_width) * x, (__colorpicker_radius + __colorpicker_width) * y, 0, 1), XMFLOAT4(rgb.x,rgb.y,rgb.z,1) });
|
|
}
|
|
|
|
GPUBufferDesc desc;
|
|
desc.BindFlags = BIND_VERTEX_BUFFER;
|
|
desc.ByteWidth = (UINT)(vertices.size() * sizeof(Vertex));
|
|
desc.CPUAccessFlags = CPU_ACCESS_WRITE;
|
|
desc.MiscFlags = 0;
|
|
desc.StructureByteStride = 0;
|
|
desc.Usage = USAGE_DYNAMIC;
|
|
SubresourceData data;
|
|
data.pSysMem = vertices.data();
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&desc, &data, &vb_hue);
|
|
}
|
|
// picker
|
|
{
|
|
float _radius = 3;
|
|
float _width = 3;
|
|
std::vector<Vertex> vertices(0);
|
|
for (float i = 0; i <= 100; i += 1.0f)
|
|
{
|
|
float p = i / 100;
|
|
float t = p * XM_2PI;
|
|
float x = cos(t);
|
|
float y = -sin(t);
|
|
vertices.push_back({ XMFLOAT4(_radius * x, _radius * y, 0, 1), XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4((_radius + _width) * x, (_radius + _width) * y, 0, 1), XMFLOAT4(1,1,1,1) });
|
|
}
|
|
|
|
GPUBufferDesc desc;
|
|
desc.BindFlags = BIND_VERTEX_BUFFER;
|
|
desc.ByteWidth = (UINT)(vertices.size() * sizeof(Vertex));
|
|
desc.CPUAccessFlags = 0;
|
|
desc.MiscFlags = 0;
|
|
desc.StructureByteStride = 0;
|
|
desc.Usage = USAGE_IMMUTABLE;
|
|
SubresourceData data;
|
|
data.pSysMem = vertices.data();
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&desc, &data, &vb_picker);
|
|
}
|
|
// preview
|
|
{
|
|
float _width = 20;
|
|
|
|
std::vector<Vertex> vertices(0);
|
|
vertices.push_back({ XMFLOAT4(-_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4(-_width, _width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4(_width, _width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4(-_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4(_width, _width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
vertices.push_back({ XMFLOAT4(_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) });
|
|
|
|
GPUBufferDesc desc;
|
|
desc.BindFlags = BIND_VERTEX_BUFFER;
|
|
desc.ByteWidth = (UINT)(vertices.size() * sizeof(Vertex));
|
|
desc.CPUAccessFlags = 0;
|
|
desc.MiscFlags = 0;
|
|
desc.StructureByteStride = 0;
|
|
desc.Usage = USAGE_IMMUTABLE;
|
|
SubresourceData data;
|
|
data.pSysMem = vertices.data();
|
|
hr = wiRenderer::GetDevice()->CreateBuffer(&desc, &data, &vb_preview);
|
|
}
|
|
|
|
}
|
|
|
|
XMMATRIX __cam = wiRenderer::GetDevice()->GetScreenProjection();
|
|
|
|
wiRenderer::GetDevice()->BindConstantBufferVS(wiRenderer::constantBuffers[CBTYPE_MISC], CBSLOT_RENDERER_MISC, threadID);
|
|
wiRenderer::GetDevice()->BindRasterizerState(wiRenderer::rasterizers[RSTYPE_DOUBLESIDED], threadID);
|
|
wiRenderer::GetDevice()->BindBlendState(wiRenderer::blendStates[BSTYPE_OPAQUE], threadID);
|
|
wiRenderer::GetDevice()->BindDepthStencilState(wiRenderer::depthStencils[DSSTYPE_XRAY], 0, threadID);
|
|
wiRenderer::GetDevice()->BindVertexLayout(wiRenderer::vertexLayouts[VLTYPE_LINE], threadID);
|
|
wiRenderer::GetDevice()->BindVS(wiRenderer::vertexShaders[VSTYPE_LINE], threadID);
|
|
wiRenderer::GetDevice()->BindPS(wiRenderer::pixelShaders[PSTYPE_LINE], threadID);
|
|
wiRenderer::GetDevice()->BindPrimitiveTopology(TRIANGLESTRIP, threadID);
|
|
|
|
wiRenderer::MiscCB cb;
|
|
|
|
// render saturation triangle
|
|
{
|
|
cb.mTransform = XMMatrixTranspose(
|
|
XMMatrixRotationZ(-angle) *
|
|
XMMatrixTranslation(translation.x + __colorpicker_center, translation.y + __colorpicker_center, 0) *
|
|
__cam
|
|
);
|
|
cb.mColor = XMFLOAT4(1, 1, 1, 1);
|
|
wiRenderer::GetDevice()->UpdateBuffer(wiRenderer::constantBuffers[CBTYPE_MISC], &cb, threadID);
|
|
const GPUBuffer* vbs[] = {
|
|
&vb_saturation,
|
|
};
|
|
const UINT strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
wiRenderer::GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, threadID);
|
|
wiRenderer::GetDevice()->Draw(vb_saturation.GetDesc().ByteWidth / sizeof(Vertex), threadID);
|
|
}
|
|
|
|
// render hue circle
|
|
{
|
|
cb.mTransform = XMMatrixTranspose(
|
|
XMMatrixTranslation(translation.x + __colorpicker_center, translation.y + __colorpicker_center, 0) *
|
|
__cam
|
|
);
|
|
cb.mColor = XMFLOAT4(1, 1, 1, 1);
|
|
wiRenderer::GetDevice()->UpdateBuffer(wiRenderer::constantBuffers[CBTYPE_MISC], &cb, threadID);
|
|
const GPUBuffer* vbs[] = {
|
|
&vb_hue,
|
|
};
|
|
const UINT strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
wiRenderer::GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, threadID);
|
|
wiRenderer::GetDevice()->Draw(vb_hue.GetDesc().ByteWidth / sizeof(Vertex), threadID);
|
|
}
|
|
|
|
// render hue picker
|
|
{
|
|
cb.mTransform = XMMatrixTranspose(
|
|
XMMatrixTranslation(hue_picker.x, hue_picker.y, 0) *
|
|
__cam
|
|
);
|
|
cb.mColor = XMFLOAT4(1 - hue_color.x, 1 - hue_color.y, 1 - hue_color.z, 1);
|
|
wiRenderer::GetDevice()->UpdateBuffer(wiRenderer::constantBuffers[CBTYPE_MISC], &cb, threadID);
|
|
const GPUBuffer* vbs[] = {
|
|
&vb_picker,
|
|
};
|
|
const UINT strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
wiRenderer::GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, threadID);
|
|
wiRenderer::GetDevice()->Draw(vb_picker.GetDesc().ByteWidth / sizeof(Vertex), threadID);
|
|
}
|
|
|
|
// render saturation picker
|
|
{
|
|
cb.mTransform = XMMatrixTranspose(
|
|
XMMatrixTranslation(saturation_picker.x, saturation_picker.y, 0) *
|
|
__cam
|
|
);
|
|
cb.mColor = XMFLOAT4(1 - final_color.x, 1 - final_color.y, 1 - final_color.z, 1);
|
|
wiRenderer::GetDevice()->UpdateBuffer(wiRenderer::constantBuffers[CBTYPE_MISC], &cb, threadID);
|
|
const GPUBuffer* vbs[] = {
|
|
&vb_picker,
|
|
};
|
|
const UINT strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
wiRenderer::GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, threadID);
|
|
wiRenderer::GetDevice()->Draw(vb_picker.GetDesc().ByteWidth / sizeof(Vertex), threadID);
|
|
}
|
|
|
|
// render preview
|
|
{
|
|
cb.mTransform = XMMatrixTranspose(
|
|
XMMatrixTranslation(translation.x + 260, translation.y + 40, 0) *
|
|
__cam
|
|
);
|
|
cb.mColor = GetPickColor();
|
|
wiRenderer::GetDevice()->UpdateBuffer(wiRenderer::constantBuffers[CBTYPE_MISC], &cb, threadID);
|
|
const GPUBuffer* vbs[] = {
|
|
&vb_preview,
|
|
};
|
|
const UINT strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
wiRenderer::GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, threadID);
|
|
wiRenderer::GetDevice()->Draw(vb_preview.GetDesc().ByteWidth / sizeof(Vertex), threadID);
|
|
}
|
|
|
|
// RGB values:
|
|
stringstream _rgb("");
|
|
_rgb << "R: " << (int)(final_color.x * 255) << endl;
|
|
_rgb << "G: " << (int)(final_color.y * 255) << endl;
|
|
_rgb << "B: " << (int)(final_color.z * 255) << endl;
|
|
wiFont(_rgb.str(), wiFontProps((int)(translation.x + 200), (int)(translation.y + 200), -1, WIFALIGN_LEFT, WIFALIGN_TOP, 2, 1,
|
|
textColor, textShadowColor)).Draw(threadID, true);
|
|
|
|
}
|
|
XMFLOAT4 wiColorPicker::GetPickColor()
|
|
{
|
|
return final_color;
|
|
}
|
|
void wiColorPicker::SetPickColor(const XMFLOAT4& value)
|
|
{
|
|
// TODO
|
|
}
|
|
void wiColorPicker::OnColorChanged(function<void(wiEventArgs args)> func)
|
|
{
|
|
onColorChanged = move(func);
|
|
}
|