whoa/src/ui/simple/CSimpleSlider.cpp

354 lines
10 KiB
C++

#include "ui/simple/CSimpleSlider.hpp"
#include "math/Utils.hpp"
#include "ui/LoadXML.hpp"
#include "ui/simple/CSimpleSliderScript.hpp"
#include "ui/simple/CSimpleTexture.hpp"
#include "util/Lua.hpp"
#include "util/StringTo.hpp"
#include <common/XML.hpp>
#include <tempest/Math.hpp>
int32_t CSimpleSlider::s_metatable;
int32_t CSimpleSlider::s_objectType;
void CSimpleSlider::CreateScriptMetaTable() {
lua_State* L = FrameScript_GetContext();
int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleSlider::RegisterScriptMethods);
CSimpleSlider::s_metatable = ref;
}
int32_t CSimpleSlider::GetObjectType() {
if (!CSimpleSlider::s_objectType) {
CSimpleSlider::s_objectType = ++FrameScript_Object::s_objectTypes;
}
return CSimpleSlider::s_objectType;
}
void CSimpleSlider::RegisterScriptMethods(lua_State* L) {
CSimpleFrame::RegisterScriptMethods(L);
FrameScript_Object::FillScriptMethodTable(L, SimpleSliderMethods, NUM_SIMPLE_SLIDER_SCRIPT_METHODS);
}
FrameScript_Object::ScriptIx* CSimpleSlider::GetScriptByName(const char* name, ScriptData& data) {
auto parentScript = CSimpleFrame::GetScriptByName(name, data);
if (parentScript) {
return parentScript;
}
if (!SStrCmpI(name, "OnValueChanged", STORM_MAX_STR)) {
data.wrapper = "return function(self,value) %s end";
return &this->m_onValueChanged;
}
if (!SStrCmpI(name, "OnMinMaxChanged", STORM_MAX_STR)) {
data.wrapper = "return function(self,min,max) %s end";
return &this->m_onMinMaxChanged;
}
return nullptr;
}
int32_t CSimpleSlider::GetScriptMetaTable() {
return CSimpleSlider::s_metatable;
}
bool CSimpleSlider::IsA(int32_t type) {
return type == CSimpleSlider::s_objectType
|| type == CSimpleFrame::s_objectType
|| type == CScriptRegion::s_objectType
|| type == CScriptObject::s_objectType;
}
void CSimpleSlider::LoadXML(const XMLNode* node, CStatus* status) {
this->CSimpleFrame::LoadXML(node, status);
// Thumb
int32_t drawLayer = DRAWLAYER_ARTWORK_OVERLAY;
auto drawLayerStr = node->GetAttributeByName("drawLayer");
if (drawLayerStr && *drawLayerStr) {
StringToDrawLayer(drawLayerStr, drawLayer);
}
for (auto child = node->m_child; child; child = child->m_next) {
if (!SStrCmpI(child->GetName(), "ThumbTexture")) {
auto thumbTexture = LoadXML_Texture(child, this, status);
this->SetThumbTexture(thumbTexture, drawLayer);
}
}
// Value step
auto valueStepStr = node->GetAttributeByName("valueStep");
auto valueStep = valueStepStr && *valueStepStr ? SStrToFloat(valueStepStr) : 0.001f;
this->SetValueStep(valueStep);
// Min, max, and default values
auto minValueStr = node->GetAttributeByName("minValue");
if (minValueStr && *minValueStr) {
auto minValue = SStrToFloat(minValueStr);
auto maxValueStr = node->GetAttributeByName("maxValue");
if (maxValueStr && *maxValueStr) {
auto maxValue = SStrToFloat(maxValueStr);
this->SetMinMaxValues(minValue, maxValue);
auto defaultValueStr = node->GetAttributeByName("defaultValue");
if (defaultValueStr && *defaultValueStr) {
auto defaultValue = SStrToFloat(defaultValueStr);
this->SetValue(defaultValue);
}
}
}
// Orientation
auto orientationStr = node->GetAttributeByName("orientation");
if (orientationStr && *orientationStr) {
ORIENTATION orientation;
if (StringToOrientation(orientationStr, orientation)) {
this->SetOrientation(orientation);
} else {
status->Add(
STATUS_WARNING,
"Frame %s: Unknown orientation %s in element %s",
this->GetDisplayName(),
orientationStr,
node->GetName()
);
}
}
}
void CSimpleSlider::OnFrameSizeChanged(const CRect& rect) {
this->CSimpleFrame::OnFrameSizeChanged(rect);
this->m_changed = 1;
}
void CSimpleSlider::OnLayerHide() {
this->m_buttonDown = 0;
this->CSimpleFrame::OnLayerHide();
}
int32_t CSimpleSlider::OnLayerMouseDown(const CMouseEvent& evt, const char* btn) {
// TODO something like CSimpleFrame::IsEnabled or CSimpleFrame::GetFrameFlag
if (!(this->m_flags & 0x400)) {
this->m_buttonDown = 1;
this->OnLayerTrackUpdate(evt);
}
return this->CSimpleFrame::OnLayerMouseDown(evt, btn);
}
int32_t CSimpleSlider::OnLayerMouseUp(const CMouseEvent& evt, const char* btn) {
this->m_buttonDown = 0;
return this->CSimpleFrame::OnLayerMouseUp(evt, btn);
}
int32_t CSimpleSlider::OnLayerTrackUpdate(const CMouseEvent& evt) {
if (this->m_buttonDown && this->m_thumbTexture) {
auto thumbTexture = this->m_thumbTexture;
float area;
float offset;
if (this->m_orientation == ORIENTATION_VERTICAL) {
area = this->m_rect.maxY - this->m_rect.minY - thumbTexture->GetHeight();
offset = this->m_rect.maxY - (thumbTexture->GetHeight() / 2.0f) - evt.y;
} else {
area = this->m_rect.maxX - this->m_rect.minX - thumbTexture->GetWidth();
offset = evt.x - ((thumbTexture->GetWidth() / 2.0f) + this->m_rect.minX);
}
float value = (this->m_range + this->m_baseValue - this->m_baseValue) * (offset / area) + this->m_baseValue;
this->SetValue(value);
}
return this->CSimpleFrame::OnLayerTrackUpdate(evt);
}
void CSimpleSlider::OnLayerUpdate(float elapsedSec) {
this->CSimpleFrame::OnLayerUpdate(elapsedSec);
if (this->m_changed && this->m_thumbTexture && this->m_rangeSet && this->m_valueSet) {
auto valueRange = this->m_range + this->m_baseValue - this->m_baseValue;
auto valueOffset = CMath::fequal(valueRange, 0.0f)
? 0.0f
: (this->m_value - this->m_baseValue) / valueRange;
if (this->m_orientation == ORIENTATION_VERTICAL) {
auto rangeY = (this->m_rect.maxY - this->m_rect.minY) / this->m_layoutScale;
auto offsetY = -((rangeY - this->m_thumbTexture->GetHeight()) * valueOffset);
this->m_thumbTexture->SetPoint(FRAMEPOINT_TOP, this, FRAMEPOINT_TOP, 0.0f, offsetY, 1);
} else {
auto rangeX = (this->m_rect.maxX - this->m_rect.minX) / this->m_layoutScale;
auto offsetX = (rangeX - this->m_thumbTexture->GetWidth()) * valueOffset;
this->m_thumbTexture->SetPoint(FRAMEPOINT_LEFT, this, FRAMEPOINT_LEFT, offsetX, 0.0f, 1);
}
this->m_changed = 0;
}
}
void CSimpleSlider::RunOnMinMaxChangedScript() {
if (!this->m_onMinMaxChanged.luaRef) {
return;
}
auto L = FrameScript_GetContext();
lua_pushnumber(L, this->m_baseValue);
lua_pushnumber(L, this->m_range + this->m_baseValue);
this->RunScript(this->m_onMinMaxChanged, 2, nullptr);
}
void CSimpleSlider::RunOnValueChangedScript() {
if (!this->m_onValueChanged.luaRef) {
return;
}
auto L = FrameScript_GetContext();
lua_pushnumber(L, this->m_value);
this->RunScript(this->m_onValueChanged, 1, nullptr);
}
void CSimpleSlider::SetMinMaxValues(float min, float max) {
float range = max - min;
if (this->m_rangeSet && AreEqual(this->m_range, range, WHOA_EPSILON_1) && AreEqual(this->m_baseValue, min, WHOA_EPSILON_1)) {
return;
}
this->m_baseValue = min;
this->m_range = range;
this->m_changed = 1;
this->m_rangeSet = 1;
this->RunOnMinMaxChangedScript();
if (this->m_valueSet) {
// Fit current value within range
this->SetValue(this->m_value);
}
}
void CSimpleSlider::SetOrientation(ORIENTATION orientation) {
this->m_orientation = orientation;
if (this->m_thumbTexture) {
this->m_thumbTexture->ClearAllPoints();
}
this->m_changed = 1;
}
void CSimpleSlider::SetThumbTexture(CSimpleTexture* texture, int32_t drawLayer) {
if (this->m_thumbTexture == texture) {
return;
}
if (this->m_thumbTexture) {
delete this->m_thumbTexture;
}
if (texture) {
texture->SetFrame(this, drawLayer, 1);
texture->ClearAllPoints();
}
this->m_changed = 1;
this->m_thumbTexture = texture;
}
void CSimpleSlider::SetValue(float value) {
if (!this->m_rangeSet) {
return;
}
auto v4 = value;
auto v5 = std::max(value, this->m_baseValue);
auto maxValue = this->m_range + this->m_baseValue;
if (maxValue >= v5) {
if (v4 < this->m_baseValue) {
v4 = this->m_baseValue;
}
} else {
v4 = maxValue;
}
auto newValue = this->StepValue(v4);
// Clamp to max value
if (newValue + this->m_valueStep > maxValue) {
newValue = maxValue;
}
// Clamp to base value
if (newValue - this->m_valueStep < this->m_baseValue) {
newValue = this->m_baseValue;
}
if (!this->m_valueSet || fabs(newValue - this->m_value) >= 0.00000023841858) {
this->m_value = newValue;
this->m_changed = 1;
this->m_valueSet = 1;
this->RunOnValueChangedScript();
}
}
void CSimpleSlider::SetValueStep(float valueStep) {
valueStep = std::max(valueStep, 0.00000011920929f);
if (CMath::fequal(this->m_valueStep, valueStep)) {
return;
}
this->m_valueStep = valueStep;
if (this->m_valueSet) {
this->SetValue(this->m_value);
}
}
float CSimpleSlider::StepValue(float value) {
if (this->m_valueStep == 0.0f) {
return value;
}
auto valueOffset = value - this->m_baseValue;
auto halfStep = this->m_valueStep / 2.0f;
if (valueOffset <= 0.0f) {
return CMath::fint((valueOffset - halfStep) / this->m_valueStep) * this->m_valueStep + this->m_baseValue;
}
return CMath::fint((valueOffset + halfStep) / this->m_valueStep) * this->m_valueStep + this->m_baseValue;
}
void CSimpleSlider::UnregisterRegion(CSimpleRegion* region) {
if (this->m_thumbTexture == region) {
this->m_thumbTexture = nullptr;
}
this->CSimpleFrame::UnregisterRegion(region);
}