Index: ps/trunk/source/gui/CButton.cpp
===================================================================
--- ps/trunk/source/gui/CButton.cpp (revision 22692)
+++ ps/trunk/source/gui/CButton.cpp (revision 22693)
@@ -1,119 +1,109 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CButton.h"
#include "gui/CGUIColor.h"
#include "lib/ogl.h"
CButton::CButton(CGUI* pGUI)
: IGUIObject(pGUI), IGUIButtonBehavior(pGUI), IGUITextOwner(pGUI)
{
AddSetting("buffer_zone");
AddSetting("caption");
AddSetting("cell_id");
AddSetting("font");
AddSetting("sound_disabled");
AddSetting("sound_enter");
AddSetting("sound_leave");
AddSetting("sound_pressed");
AddSetting("sound_released");
AddSetting("sprite");
AddSetting("sprite_over");
AddSetting("sprite_pressed");
AddSetting("sprite_disabled");
AddSetting("text_align");
AddSetting("text_valign");
AddSetting("textcolor");
AddSetting("textcolor_over");
AddSetting("textcolor_pressed");
AddSetting("textcolor_disabled");
AddSetting("tooltip");
AddSetting("tooltip_style");
AddText();
}
CButton::~CButton()
{
}
void CButton::SetupText()
{
ENSURE(m_GeneratedTexts.size() == 1);
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
// Use the default if none is specified
// TODO Gee: (2004-08-14) Default should not be hard-coded, but be in styles!
font = L"default";
- CGUIString* caption = nullptr;
- GUI::GetSettingPointer(this, "caption", caption);
+ const CGUIString& caption = GUI::GetSetting(this, "caption");
float buffer_zone = 0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
- m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, m_CachedActualSize.GetWidth(), buffer_zone, this);
+ m_GeneratedTexts[0] = CGUIText(m_pGUI, caption, font, m_CachedActualSize.GetWidth(), buffer_zone, this);
CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]);
}
void CButton::HandleMessage(SGUIMessage& Message)
{
// Important
IGUIButtonBehavior::HandleMessage(Message);
IGUITextOwner::HandleMessage(Message);
}
void CButton::Draw()
{
float bz = GetBufferedZ();
- CGUISpriteInstance* sprite;
- CGUISpriteInstance* sprite_over;
- CGUISpriteInstance* sprite_pressed;
- CGUISpriteInstance* sprite_disabled;
int cell_id;
// Statically initialise some strings, so we don't have to do
// lots of allocation every time this function is called
static const CStr strSprite("sprite");
static const CStr strSpriteOver("sprite_over");
static const CStr strSpritePressed("sprite_pressed");
static const CStr strSpriteDisabled("sprite_disabled");
static const CStr strCellId("cell_id");
- GUI::GetSettingPointer(this, strSprite, sprite);
- GUI::GetSettingPointer(this, strSpriteOver, sprite_over);
- GUI::GetSettingPointer(this, strSpritePressed, sprite_pressed);
- GUI::GetSettingPointer(this, strSpriteDisabled, sprite_disabled);
+ CGUISpriteInstance& sprite = GUI::GetSetting(this, strSprite);
+ CGUISpriteInstance& sprite_over = GUI::GetSetting(this, strSpriteOver);
+ CGUISpriteInstance& sprite_pressed = GUI::GetSetting(this, strSpritePressed);
+ CGUISpriteInstance& sprite_disabled = GUI::GetSetting(this, strSpriteDisabled);
+
GUI::GetSetting(this, strCellId, cell_id);
- DrawButton(m_CachedActualSize,
- bz,
- *sprite,
- *sprite_over,
- *sprite_pressed,
- *sprite_disabled,
- cell_id);
+ DrawButton(m_CachedActualSize, bz, sprite, sprite_over, sprite_pressed, sprite_disabled, cell_id);
CGUIColor color = ChooseColor();
DrawText(0, color, m_TextPos, bz+0.1f);
}
Index: ps/trunk/source/gui/CChart.cpp
===================================================================
--- ps/trunk/source/gui/CChart.cpp (revision 22692)
+++ ps/trunk/source/gui/CChart.cpp (revision 22693)
@@ -1,324 +1,321 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CChart.h"
#include "gui/CGUIColor.h"
#include "gui/CGUIString.h"
#include "gui/GUIMatrix.h"
#include "graphics/ShaderManager.h"
#include "i18n/L10n.h"
#include "lib/ogl.h"
#include "ps/CLogger.h"
#include "renderer/Renderer.h"
#include "third_party/cppformat/format.h"
#include
CChart::CChart(CGUI* pGUI)
: IGUIObject(pGUI), IGUITextOwner(pGUI)
{
AddSetting("axis_color");
AddSetting("axis_width");
AddSetting("buffer_zone");
AddSetting("font");
AddSetting("format_x");
AddSetting("format_y");
AddSetting("series_color");
AddSetting("series");
AddSetting("text_align");
GUI::GetSetting(this, "axis_width", m_AxisWidth);
GUI::GetSetting(this, "format_x", m_FormatX);
GUI::GetSetting(this, "format_y", m_FormatY);
}
CChart::~CChart()
{
}
void CChart::HandleMessage(SGUIMessage& Message)
{
// TODO: implement zoom
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
{
GUI::GetSetting(this, "axis_width", m_AxisWidth);
GUI::GetSetting(this, "format_x", m_FormatX);
GUI::GetSetting(this, "format_y", m_FormatY);
UpdateSeries();
break;
}
}
}
void CChart::DrawLine(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector& vertices) const
{
shader->Uniform(str_color, color);
shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
shader->AssertPointersBound();
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.1f);
if (!g_Renderer.m_SkipSubmit)
glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3);
glLineWidth(1.0f);
glDisable(GL_LINE_SMOOTH);
}
void CChart::DrawTriangleStrip(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector& vertices) const
{
shader->Uniform(str_color, color);
shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]);
shader->AssertPointersBound();
if (!g_Renderer.m_SkipSubmit)
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size() / 3);
}
void CChart::DrawAxes(const CShaderProgramPtr& shader) const
{
const float bz = GetBufferedZ();
CRect rect = GetChartRect();
std::vector vertices;
vertices.reserve(30);
#define ADD(x, y) vertices.push_back(x); vertices.push_back(y); vertices.push_back(bz + 0.5f);
ADD(m_CachedActualSize.right, m_CachedActualSize.bottom);
ADD(rect.right + m_AxisWidth, rect.bottom);
ADD(m_CachedActualSize.left, m_CachedActualSize.bottom);
ADD(rect.left, rect.bottom);
ADD(m_CachedActualSize.left, m_CachedActualSize.top);
ADD(rect.left, rect.top - m_AxisWidth);
#undef ADD
CGUIColor axis_color(0.5f, 0.5f, 0.5f, 1.f);
GUI::GetSetting(this, "axis_color", axis_color);
DrawTriangleStrip(shader, axis_color, vertices);
}
void CChart::Draw()
{
PROFILE3("render chart");
if (m_Series.empty())
return;
const float bz = GetBufferedZ();
CRect rect = GetChartRect();
const float width = rect.GetWidth();
const float height = rect.GetHeight();
// Disable depth updates to prevent apparent z-fighting-related issues
// with some drivers causing units to get drawn behind the texture.
glDepthMask(0);
// Setup the render state
CMatrix3D transform = GetDefaultGuiMatrix();
CShaderDefines lineDefines;
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid, g_Renderer.GetSystemShaderDefines(), lineDefines);
tech->BeginPass();
CShaderProgramPtr shader = tech->GetShader();
shader->Uniform(str_transform, transform);
CVector2D scale(width / (m_RightTop.X - m_LeftBottom.X), height / (m_RightTop.Y - m_LeftBottom.Y));
for (const CChartData& data : m_Series)
{
if (data.m_Points.empty())
continue;
std::vector vertices;
for (const CVector2D& point : data.m_Points)
{
if (fabs(point.X) != std::numeric_limits::infinity() && fabs(point.Y) != std::numeric_limits::infinity())
{
vertices.push_back(rect.left + (point.X - m_LeftBottom.X) * scale.X);
vertices.push_back(rect.bottom - (point.Y - m_LeftBottom.Y) * scale.Y);
vertices.push_back(bz + 0.5f);
}
else
{
DrawLine(shader, data.m_Color, vertices);
vertices.clear();
}
}
if (!vertices.empty())
DrawLine(shader, data.m_Color, vertices);
}
if (m_AxisWidth > 0)
DrawAxes(shader);
tech->EndPass();
// Reset depth mask
glDepthMask(1);
for (size_t i = 0; i < m_TextPositions.size(); ++i)
DrawText(i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i], bz + 0.5f);
}
CRect CChart::GetChartRect() const
{
return CRect(
m_CachedActualSize.TopLeft() + CPos(m_AxisWidth, m_AxisWidth),
m_CachedActualSize.BottomRight() - CPos(m_AxisWidth, m_AxisWidth)
);
}
void CChart::UpdateSeries()
{
- CGUISeries* pSeries;
- GUI::GetSettingPointer(this, "series", pSeries);
-
- CGUIList* pSeriesColor;
- GUI::GetSettingPointer(this, "series_color", pSeriesColor);
+ const CGUISeries& pSeries = GUI::GetSetting(this, "series");
+ const CGUIList& pSeriesColor = GUI::GetSetting(this, "series_color");
m_Series.clear();
- m_Series.resize(pSeries->m_Series.size());
- for (size_t i = 0; i < pSeries->m_Series.size(); ++i)
+ m_Series.resize(pSeries.m_Series.size());
+ for (size_t i = 0; i < pSeries.m_Series.size(); ++i)
{
CChartData& data = m_Series[i];
- if (i < pSeriesColor->m_Items.size() && !data.m_Color.ParseString(m_pGUI, pSeriesColor->m_Items[i].GetOriginalString().ToUTF8(), 0))
- LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(pSeriesColor->m_Items[i].GetOriginalString()));
+ if (i < pSeriesColor.m_Items.size() && !data.m_Color.ParseString(m_pGUI, pSeriesColor.m_Items[i].GetOriginalString().ToUTF8(), 0))
+ LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(pSeriesColor.m_Items[i].GetOriginalString()));
- data.m_Points = pSeries->m_Series[i];
+ data.m_Points = pSeries.m_Series[i];
}
UpdateBounds();
SetupText();
}
void CChart::SetupText()
{
m_GeneratedTexts.clear();
m_TextPositions.clear();
if (m_Series.empty())
return;
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
font = L"default";
float buffer_zone = 0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
// Add Y-axis
GUI::GetSetting(this, "format_y", m_FormatY);
const float height = GetChartRect().GetHeight();
// TODO: split values depend on the format;
if (m_EqualY)
{
// We don't need to generate many items for equal values
AddFormattedValue(m_FormatY, m_RightTop.Y, font, buffer_zone);
m_TextPositions.emplace_back(GetChartRect().TopLeft());
}
else
for (int i = 0; i < 3; ++i)
{
AddFormattedValue(m_FormatY, m_RightTop.Y - (m_RightTop.Y - m_LeftBottom.Y) / 3.f * i, font, buffer_zone);
m_TextPositions.emplace_back(GetChartRect().TopLeft() + CPos(0.f, height / 3.f * i));
}
// Add X-axis
GUI::GetSetting(this, "format_x", m_FormatX);
const float width = GetChartRect().GetWidth();
if (m_EqualX)
{
CSize text_size = AddFormattedValue(m_FormatX, m_RightTop.X, font, buffer_zone);
m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size);
}
else
for (int i = 0; i < 3; ++i)
{
CSize text_size = AddFormattedValue(m_FormatX, m_RightTop.X - (m_RightTop.X - m_LeftBottom.X) / 3 * i, font, buffer_zone);
m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size - CPos(width / 3 * i, 0.f));
}
}
CSize CChart::AddFormattedValue(const CStrW& format, const float value, const CStrW& font, const float buffer_zone)
{
// TODO: we need to catch cases with equal formatted values.
CGUIString gui_str;
if (format == L"DECIMAL2")
{
wchar_t buffer[64];
swprintf(buffer, 64, L"%.2f", value);
gui_str.SetValue(buffer);
}
else if (format == L"INTEGER")
{
wchar_t buffer[64];
swprintf(buffer, 64, L"%d", std::lround(value));
gui_str.SetValue(buffer);
}
else if (format == L"DURATION_SHORT")
{
const int seconds = value;
wchar_t buffer[64];
swprintf(buffer, 64, L"%d:%02d", seconds / 60, seconds % 60);
gui_str.SetValue(buffer);
}
else if (format == L"PERCENTAGE")
{
wchar_t buffer[64];
swprintf(buffer, 64, L"%d%%", std::lround(value));
gui_str.SetValue(buffer);
}
else
{
LOGERROR("Unsupported chart format: " + format.EscapeToPrintableASCII());
return CSize();
}
return AddText(gui_str, font, 0, buffer_zone, this).GetSize();
}
void CChart::UpdateBounds()
{
if (m_Series.empty() || m_Series[0].m_Points.empty())
{
m_LeftBottom = m_RightTop = CVector2D(0.f, 0.f);
return;
}
m_LeftBottom = m_RightTop = m_Series[0].m_Points[0];
for (const CChartData& data : m_Series)
for (const CVector2D& point : data.m_Points)
{
if (fabs(point.X) != std::numeric_limits::infinity() && point.X < m_LeftBottom.X)
m_LeftBottom.X = point.X;
if (fabs(point.Y) != std::numeric_limits::infinity() && point.Y < m_LeftBottom.Y)
m_LeftBottom.Y = point.Y;
if (fabs(point.X) != std::numeric_limits::infinity() && point.X > m_RightTop.X)
m_RightTop.X = point.X;
if (fabs(point.Y) != std::numeric_limits::infinity() && point.Y > m_RightTop.Y)
m_RightTop.Y = point.Y;
}
m_EqualY = m_RightTop.Y == m_LeftBottom.Y;
if (m_EqualY)
m_RightTop.Y += 1;
m_EqualX = m_RightTop.X == m_LeftBottom.X;
if (m_EqualX)
m_RightTop.X += 1;
}
Index: ps/trunk/source/gui/CCheckBox.cpp
===================================================================
--- ps/trunk/source/gui/CCheckBox.cpp (revision 22692)
+++ ps/trunk/source/gui/CCheckBox.cpp (revision 22693)
@@ -1,147 +1,131 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CCheckBox.h"
#include "gui/CGUIColor.h"
#include "graphics/FontMetrics.h"
#include "ps/CLogger.h"
#include "ps/CStrIntern.h"
/**
* TODO: Since there is no call to DrawText, the checkbox won't render any text.
* Thus the font, caption, textcolor and other settings have no effect.
*/
CCheckBox::CCheckBox(CGUI* pGUI)
: IGUIObject(pGUI), IGUITextOwner(pGUI), IGUIButtonBehavior(pGUI)
{
AddSetting("buffer_zone");
AddSetting("caption");
AddSetting("cell_id");
AddSetting("checked");
AddSetting("font");
AddSetting("sound_disabled");
AddSetting("sound_enter");
AddSetting("sound_leave");
AddSetting("sound_pressed");
AddSetting("sound_released");
AddSetting("sprite");
AddSetting("sprite_over");
AddSetting("sprite_pressed");
AddSetting("sprite_disabled");
AddSetting("sprite2");
AddSetting("sprite2_over");
AddSetting("sprite2_pressed");
AddSetting("sprite2_disabled");
AddSetting("square_side");
AddSetting("textcolor");
AddSetting("textcolor_over");
AddSetting("textcolor_pressed");
AddSetting("textcolor_disabled");
AddSetting("tooltip");
AddSetting("tooltip_style");
AddText();
}
CCheckBox::~CCheckBox()
{
}
void CCheckBox::SetupText()
{
ENSURE(m_GeneratedTexts.size() == 1);
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
// Use the default if none is specified
// TODO Gee: (2004-08-14) Default should not be hard-coded, but be in styles!
font = L"default";
float square_side;
GUI::GetSetting(this, "square_side", square_side);
- CGUIString* caption = nullptr;
- GUI::GetSettingPointer(this, "caption", caption);
+ const CGUIString& caption = GUI::GetSetting(this, "caption");
float buffer_zone = 0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
- m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, m_CachedActualSize.GetWidth() - square_side, 0.f, this);
+ m_GeneratedTexts[0] = CGUIText(m_pGUI, caption, font, m_CachedActualSize.GetWidth() - square_side, 0.f, this);
}
void CCheckBox::HandleMessage(SGUIMessage& Message)
{
// Important
IGUIButtonBehavior::HandleMessage(Message);
IGUITextOwner::HandleMessage(Message);
switch (Message.type)
{
case GUIM_PRESSED:
{
bool checked;
// Switch to opposite.
GUI::GetSetting(this, "checked", checked);
checked = !checked;
GUI::SetSetting(this, "checked", checked);
break;
}
default:
break;
}
}
void CCheckBox::Draw()
{
- float bz = GetBufferedZ();
- bool checked;
- int cell_id;
- CGUISpriteInstance* sprite;
- CGUISpriteInstance* sprite_over;
- CGUISpriteInstance* sprite_pressed;
- CGUISpriteInstance* sprite_disabled;
-
- GUI::GetSetting(this, "checked", checked);
- GUI::GetSetting(this, "cell_id", cell_id);
-
- if (checked)
- {
- GUI::GetSettingPointer(this, "sprite2", sprite);
- GUI::GetSettingPointer(this, "sprite2_over", sprite_over);
- GUI::GetSettingPointer(this, "sprite2_pressed", sprite_pressed);
- GUI::GetSettingPointer(this, "sprite2_disabled", sprite_disabled);
- }
+ if (GUI::GetSetting(this, "checked"))
+ DrawButton(
+ m_CachedActualSize,
+ GetBufferedZ(),
+ GUI::GetSetting(this, "sprite2"),
+ GUI::GetSetting(this, "sprite2_over"),
+ GUI::GetSetting(this, "sprite2_pressed"),
+ GUI::GetSetting(this, "sprite2_disabled"),
+ GUI::GetSetting(this, "cell_id"));
else
- {
- GUI::GetSettingPointer(this, "sprite", sprite);
- GUI::GetSettingPointer(this, "sprite_over", sprite_over);
- GUI::GetSettingPointer(this, "sprite_pressed", sprite_pressed);
- GUI::GetSettingPointer(this, "sprite_disabled", sprite_disabled);
- }
-
- DrawButton(m_CachedActualSize,
- bz,
- *sprite,
- *sprite_over,
- *sprite_pressed,
- *sprite_disabled,
- cell_id);
+ DrawButton(
+ m_CachedActualSize,
+ GetBufferedZ(),
+ GUI::GetSetting(this, "sprite"),
+ GUI::GetSetting(this, "sprite_over"),
+ GUI::GetSetting(this, "sprite_pressed"),
+ GUI::GetSetting(this, "sprite_disabled"),
+ GUI::GetSetting(this, "cell_id"));
}
Index: ps/trunk/source/gui/CDropDown.cpp
===================================================================
--- ps/trunk/source/gui/CDropDown.cpp (revision 22692)
+++ ps/trunk/source/gui/CDropDown.cpp (revision 22693)
@@ -1,559 +1,552 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CDropDown.h"
#include "gui/CGUIColor.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
#include "soundmanager/ISoundManager.h"
CDropDown::CDropDown(CGUI* pGUI)
: CList(pGUI), IGUIObject(pGUI),
m_Open(false), m_HideScrollBar(false), m_ElementHighlight(-1)
{
AddSetting("button_width");
AddSetting("dropdown_size");
AddSetting("dropdown_buffer");
AddSetting("minimum_visible_items");
// AddSetting("sound_closed");
AddSetting("sound_disabled");
AddSetting("sound_enter");
AddSetting("sound_leave");
AddSetting("sound_opened");
AddSetting("sprite"); // Background that sits around the size
AddSetting("sprite_disabled");
AddSetting("sprite_list"); // Background of the drop down list
AddSetting("sprite2"); // Button that sits to the right
AddSetting("sprite2_over");
AddSetting("sprite2_pressed");
AddSetting("sprite2_disabled");
AddSetting("text_valign");
// Add these in CList! And implement TODO
//AddSetting("textcolor_over");
//AddSetting("textcolor_pressed");
AddSetting("textcolor_selected");
AddSetting("textcolor_disabled");
// Scrollbar is forced to be true.
GUI::SetSetting(this, "scrollbar", true);
}
CDropDown::~CDropDown()
{
}
void CDropDown::SetupText()
{
SetupListRect();
CList::SetupText();
}
void CDropDown::UpdateCachedSize()
{
CList::UpdateCachedSize();
SetupText();
}
void CDropDown::HandleMessage(SGUIMessage& Message)
{
// Important
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
{
// Update cached list rect
if (Message.value == "size" ||
Message.value == "absolute" ||
Message.value == "dropdown_size" ||
Message.value == "dropdown_buffer" ||
Message.value == "minimum_visible_items" ||
Message.value == "scrollbar_style" ||
Message.value == "button_width")
{
SetupListRect();
}
break;
}
case GUIM_MOUSE_MOTION:
{
if (!m_Open)
break;
CPos mouse = m_pGUI->GetMousePos();
if (!GetListRect().PointInside(mouse))
break;
bool scrollbar;
- CGUIList* pList;
+ const CGUIList& pList = GUI::GetSetting(this, "list");
GUI::GetSetting(this, "scrollbar", scrollbar);
- GUI::GetSettingPointer(this, "list", pList);
float scroll = 0.f;
if (scrollbar)
scroll = GetScrollBar(0).GetPos();
CRect rect = GetListRect();
mouse.y += scroll;
int set = -1;
- for (int i = 0; i < (int)pList->m_Items.size(); ++i)
+ for (int i = 0; i < static_cast(pList.m_Items.size()); ++i)
{
if (mouse.y >= rect.top + m_ItemsYPositions[i] &&
mouse.y < rect.top + m_ItemsYPositions[i+1] &&
// mouse is not over scroll-bar
(m_HideScrollBar ||
mouse.x < GetScrollBar(0).GetOuterRect().left ||
mouse.x > GetScrollBar(0).GetOuterRect().right))
{
set = i;
}
}
if (set != -1)
{
m_ElementHighlight = set;
//UpdateAutoScroll();
}
break;
}
case GUIM_MOUSE_ENTER:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_enter", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
case GUIM_MOUSE_LEAVE:
{
GUI::GetSetting(this, "selected", m_ElementHighlight);
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_leave", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
// We can't inherent this routine from CList, because we need to include
// a mouse click to open the dropdown, also the coordinates are changed.
case GUIM_MOUSE_PRESS_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_disabled", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
if (!m_Open)
{
- CGUIList* pList;
- GUI::GetSettingPointer(this, "list", pList);
- if (pList->m_Items.empty())
+ const CGUIList& pList = GUI::GetSetting(this, "list");
+ if (pList.m_Items.empty())
return;
m_Open = true;
GetScrollBar(0).SetZ(GetBufferedZ());
GUI::GetSetting(this, "selected", m_ElementHighlight);
// Start at the position of the selected item, if possible.
GetScrollBar(0).SetPos(m_ItemsYPositions.empty() ? 0 : m_ItemsYPositions[m_ElementHighlight] - 60);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_opened", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
return; // overshadow
}
else
{
const CPos& mouse = m_pGUI->GetMousePos();
// If the regular area is pressed, then abort, and close.
if (m_CachedActualSize.PointInside(mouse))
{
m_Open = false;
GetScrollBar(0).SetZ(GetBufferedZ());
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
return; // overshadow
}
if (m_HideScrollBar ||
mouse.x < GetScrollBar(0).GetOuterRect().left ||
mouse.x > GetScrollBar(0).GetOuterRect().right ||
mouse.y < GetListRect().top)
{
m_Open = false;
GetScrollBar(0).SetZ(GetBufferedZ());
}
}
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
// Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
if (m_Open || !enabled)
break;
GUI::GetSetting(this, "selected", m_ElementHighlight);
if (m_ElementHighlight + 1 >= (int)m_ItemsYPositions.size() - 1)
break;
++m_ElementHighlight;
GUI::SetSetting(this, "selected", m_ElementHighlight);
break;
}
case GUIM_MOUSE_WHEEL_UP:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
// Don't switch elements by scrolling when open, causes a confusing interaction between this and the scrollbar.
if (m_Open || !enabled)
break;
GUI::GetSetting(this, "selected", m_ElementHighlight);
if (m_ElementHighlight - 1 < 0)
break;
m_ElementHighlight--;
GUI::SetSetting(this, "selected", m_ElementHighlight);
break;
}
case GUIM_LOST_FOCUS:
{
if (m_Open)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
m_Open = false;
break;
}
case GUIM_LOAD:
SetupListRect();
break;
default:
break;
}
// Important that this is after, so that overshadowed implementations aren't processed
CList::HandleMessage(Message);
// As HandleMessage functions return void, we need to manually verify
// whether the child list's items were modified.
if (CList::GetModified())
SetupText();
}
InReaction CDropDown::ManuallyHandleEvent(const SDL_Event_* ev)
{
InReaction result = IN_PASS;
bool update_highlight = false;
if (ev->ev.type == SDL_KEYDOWN)
{
int szChar = ev->ev.key.keysym.sym;
switch (szChar)
{
case '\r':
m_Open = false;
result = IN_HANDLED;
break;
case SDLK_HOME:
case SDLK_END:
case SDLK_UP:
case SDLK_DOWN:
case SDLK_PAGEUP:
case SDLK_PAGEDOWN:
if (!m_Open)
return IN_PASS;
// Set current selected item to highlighted, before
// then really processing these in CList::ManuallyHandleEvent()
GUI::SetSetting(this, "selected", m_ElementHighlight);
update_highlight = true;
break;
default:
// If we have inputed a character try to get the closest element to it.
// TODO: not too nice and doesn't deal with dashes.
if (m_Open && ((szChar >= SDLK_a && szChar <= SDLK_z) || szChar == SDLK_SPACE
|| (szChar >= SDLK_0 && szChar <= SDLK_9)
|| (szChar >= SDLK_KP_0 && szChar <= SDLK_KP_9)))
{
// arbitrary 1 second limit to add to string or start fresh.
// maximal amount of characters is 100, which imo is far more than enough.
if (timer_Time() - m_TimeOfLastInput > 1.0 || m_InputBuffer.length() >= 100)
m_InputBuffer = szChar;
else
m_InputBuffer += szChar;
m_TimeOfLastInput = timer_Time();
- CGUIList* pList;
- GUI::GetSettingPointer(this, "list", pList);
+ const CGUIList& pList = GUI::GetSetting(this, "list");
// let's look for the closest element
// basically it's alphabetic order and "as many letters as we can get".
int closest = -1;
int bestIndex = -1;
int difference = 1250;
- for (int i = 0; i < (int)pList->m_Items.size(); ++i)
+ for (int i = 0; i < static_cast(pList.m_Items.size()); ++i)
{
int indexOfDifference = 0;
int diff = 0;
for (size_t j = 0; j < m_InputBuffer.length(); ++j)
{
- diff = std::abs((int)(pList->m_Items[i].GetRawString().LowerCase()[j]) - (int)m_InputBuffer[j]);
+ diff = std::abs(static_cast(pList.m_Items[i].GetRawString().LowerCase()[j]) - static_cast(m_InputBuffer[j]));
if (diff == 0)
indexOfDifference = j+1;
else
break;
}
if (indexOfDifference > bestIndex || (indexOfDifference >= bestIndex && diff < difference))
{
bestIndex = indexOfDifference;
closest = i;
difference = diff;
}
}
// let's select the closest element. There should basically always be one.
if (closest != -1)
{
GUI::SetSetting(this, "selected", closest);
update_highlight = true;
GetScrollBar(0).SetPos(m_ItemsYPositions[closest] - 60);
}
result = IN_HANDLED;
}
break;
}
}
if (CList::ManuallyHandleEvent(ev) == IN_HANDLED)
result = IN_HANDLED;
if (update_highlight)
GUI::GetSetting(this, "selected", m_ElementHighlight);
return result;
}
void CDropDown::SetupListRect()
{
extern int g_yres;
extern float g_GuiScale;
float size, buffer, yres;
yres = g_yres / g_GuiScale;
u32 minimumVisibleItems;
GUI::GetSetting(this, "dropdown_size", size);
GUI::GetSetting(this, "dropdown_buffer", buffer);
GUI::GetSetting(this, "minimum_visible_items", minimumVisibleItems);
if (m_ItemsYPositions.empty())
{
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + buffer,
m_CachedActualSize.right, m_CachedActualSize.bottom + buffer + size);
m_HideScrollBar = false;
}
// Too many items so use a scrollbar
else if (m_ItemsYPositions.back() > size)
{
// Place items below if at least some items can be placed below
if (m_CachedActualSize.bottom + buffer + size <= yres)
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + buffer,
m_CachedActualSize.right, m_CachedActualSize.bottom + buffer + size);
else if ((m_ItemsYPositions.size() > minimumVisibleItems && yres - m_CachedActualSize.bottom - buffer >= m_ItemsYPositions[minimumVisibleItems]) ||
m_CachedActualSize.top < yres - m_CachedActualSize.bottom)
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + buffer,
m_CachedActualSize.right, yres);
// Not enough space below, thus place items above
else
m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - buffer - size),
m_CachedActualSize.right, m_CachedActualSize.top - buffer);
m_HideScrollBar = false;
}
else
{
// Enough space below, no scrollbar needed
if (m_CachedActualSize.bottom + buffer + m_ItemsYPositions.back() <= yres)
{
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + buffer,
m_CachedActualSize.right, m_CachedActualSize.bottom + buffer + m_ItemsYPositions.back());
m_HideScrollBar = true;
}
// Enough space below for some items, but not all, so place items below and use a scrollbar
else if ((m_ItemsYPositions.size() > minimumVisibleItems && yres - m_CachedActualSize.bottom - buffer >= m_ItemsYPositions[minimumVisibleItems]) ||
m_CachedActualSize.top < yres - m_CachedActualSize.bottom)
{
m_CachedListRect = CRect(m_CachedActualSize.left, m_CachedActualSize.bottom + buffer,
m_CachedActualSize.right, yres);
m_HideScrollBar = false;
}
// Not enough space below, thus place items above. Hide the scrollbar accordingly
else
{
m_CachedListRect = CRect(m_CachedActualSize.left, std::max(0.f, m_CachedActualSize.top - buffer - m_ItemsYPositions.back()),
m_CachedActualSize.right, m_CachedActualSize.top - buffer);
m_HideScrollBar = m_CachedActualSize.top > m_ItemsYPositions.back() + buffer;
}
}
}
CRect CDropDown::GetListRect() const
{
return m_CachedListRect;
}
bool CDropDown::MouseOver()
{
if (m_Open)
{
CRect rect(m_CachedActualSize.left, std::min(m_CachedActualSize.top, GetListRect().top),
m_CachedActualSize.right, std::max(m_CachedActualSize.bottom, GetListRect().bottom));
return rect.PointInside(m_pGUI->GetMousePos());
}
else
return m_CachedActualSize.PointInside(m_pGUI->GetMousePos());
}
void CDropDown::Draw()
{
float bz = GetBufferedZ();
float dropdown_size, button_width;
GUI::GetSetting(this, "dropdown_size", dropdown_size);
GUI::GetSetting(this, "button_width", button_width);
- CGUISpriteInstance* sprite;
- CGUISpriteInstance* sprite2;
- CGUISpriteInstance* sprite2_second;
int cell_id, selected = 0;
CGUIColor color;
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
- GUI::GetSettingPointer(this, "sprite2", sprite2);
+ CGUISpriteInstance& sprite = GUI::GetSetting(this, enabled ? "sprite" : "sprite_disabled");
+ CGUISpriteInstance& sprite2 = GUI::GetSetting(this, "sprite2");
+
GUI::GetSetting(this, "cell_id", cell_id);
GUI::GetSetting(this, "selected", selected);
GUI::GetSetting(this, enabled ? "textcolor_selected" : "textcolor_disabled", color);
- GUI::GetSettingPointer(this, enabled ? "sprite" : "sprite_disabled", sprite);
- m_pGUI->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
+ m_pGUI->DrawSprite(sprite, cell_id, bz, m_CachedActualSize);
if (button_width > 0.f)
{
CRect rect(m_CachedActualSize.right-button_width, m_CachedActualSize.top,
m_CachedActualSize.right, m_CachedActualSize.bottom);
if (!enabled)
{
- GUI::GetSettingPointer(this, "sprite2_disabled", sprite2_second);
- m_pGUI->DrawSprite(*sprite2_second || *sprite2, cell_id, bz + 0.05f, rect);
+ CGUISpriteInstance& sprite2_second = GUI::GetSetting(this, "sprite2_disabled");
+ m_pGUI->DrawSprite(sprite2_second || sprite2, cell_id, bz + 0.05f, rect);
}
else if (m_Open)
{
- GUI::GetSettingPointer(this, "sprite2_pressed", sprite2_second);
- m_pGUI->DrawSprite(*sprite2_second || *sprite2, cell_id, bz + 0.05f, rect);
+ CGUISpriteInstance& sprite2_second = GUI::GetSetting(this, "sprite2_pressed");
+ m_pGUI->DrawSprite(sprite2_second || sprite2, cell_id, bz + 0.05f, rect);
}
else if (m_MouseHovering)
{
- GUI::GetSettingPointer(this, "sprite2_over", sprite2_second);
- m_pGUI->DrawSprite(*sprite2_second || *sprite2, cell_id, bz + 0.05f, rect);
+ CGUISpriteInstance& sprite2_second = GUI::GetSetting(this, "sprite2_over");
+ m_pGUI->DrawSprite(sprite2_second || sprite2, cell_id, bz + 0.05f, rect);
}
else
- m_pGUI->DrawSprite(*sprite2, cell_id, bz + 0.05f, rect);
+ m_pGUI->DrawSprite(sprite2, cell_id, bz + 0.05f, rect);
}
if (selected != -1) // TODO: Maybe check validity completely?
{
CRect cliparea(m_CachedActualSize.left, m_CachedActualSize.top,
m_CachedActualSize.right-button_width, m_CachedActualSize.bottom);
CPos pos(m_CachedActualSize.left, m_CachedActualSize.top);
DrawText(selected, color, pos, bz+0.1f, cliparea);
}
- bool* scrollbar = NULL;
- bool old;
- GUI::GetSettingPointer(this, "scrollbar", scrollbar);
-
- old = *scrollbar;
+ // Disable scrollbar during drawing without sending a setting-changed message
+ bool& scrollbar = GUI::GetSetting(this, "scrollbar");
+ bool old = scrollbar;
if (m_Open)
{
if (m_HideScrollBar)
- *scrollbar = false;
+ scrollbar = false;
DrawList(m_ElementHighlight, "sprite_list", "sprite_selectarea", "textcolor");
if (m_HideScrollBar)
- *scrollbar = old;
+ scrollbar = old;
}
}
// When a dropdown list is opened, it needs to be visible above all the other
// controls on the page. The only way I can think of to do this is to increase
// its z value when opened, so that it's probably on top.
float CDropDown::GetBufferedZ() const
{
float bz = CList::GetBufferedZ();
if (m_Open)
return std::min(bz + 500.f, 1000.f); // TODO - don't use magic number for max z value
else
return bz;
}
Index: ps/trunk/source/gui/CImage.cpp
===================================================================
--- ps/trunk/source/gui/CImage.cpp (revision 22692)
+++ ps/trunk/source/gui/CImage.cpp (revision 22693)
@@ -1,49 +1,48 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CImage.h"
#include "GUI.h"
#include "lib/ogl.h"
CImage::CImage(CGUI* pGUI)
: IGUIObject(pGUI)
{
AddSetting("sprite");
AddSetting("cell_id");
AddSetting("tooltip");
AddSetting("tooltip_style");
}
CImage::~CImage()
{
}
void CImage::Draw()
{
float bz = GetBufferedZ();
- CGUISpriteInstance* sprite;
int cell_id;
- GUI::GetSettingPointer(this, "sprite", sprite);
GUI::GetSetting(this, "cell_id", cell_id);
- GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
+ CGUISpriteInstance& sprite = GUI::GetSetting(this, "sprite");
+ m_pGUI->DrawSprite(sprite, cell_id, bz, m_CachedActualSize);
}
Index: ps/trunk/source/gui/CInput.cpp
===================================================================
--- ps/trunk/source/gui/CInput.cpp (revision 22692)
+++ ps/trunk/source/gui/CInput.cpp (revision 22693)
@@ -1,2141 +1,2130 @@
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "CInput.h"
#include "gui/CGUIScrollBarVertical.h"
#include "gui/GUI.h"
#include "graphics/FontMetrics.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextRenderer.h"
#include "lib/ogl.h"
#include "lib/sysdep/clipboard.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/GameSetup/Config.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "renderer/Renderer.h"
#include
extern int g_yres;
CInput::CInput(CGUI* pGUI)
: IGUIObject(pGUI), IGUIScrollBarOwner(pGUI),
m_iBufferPos(-1), m_iBufferPos_Tail(-1), m_SelectingText(false), m_HorizontalScroll(0.f),
m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5), m_ComposingText(false),
m_iComposedLength(0), m_iComposedPos(0), m_iInsertPos(0), m_Readonly(false)
{
AddSetting("buffer_position");
AddSetting("buffer_zone");
AddSetting("caption");
AddSetting("cell_id");
AddSetting("font");
AddSetting("mask_char");
AddSetting("mask");
AddSetting("max_length");
AddSetting("multiline");
AddSetting("readonly");
AddSetting("scrollbar");
AddSetting("scrollbar_style");
AddSetting("sprite");
AddSetting("sprite_selectarea");
AddSetting("textcolor");
AddSetting("textcolor_selected");
AddSetting("tooltip");
AddSetting("tooltip_style");
CFG_GET_VAL("gui.cursorblinkrate", m_CursorBlinkRate);
CGUIScrollBarVertical* bar = new CGUIScrollBarVertical(pGUI);
bar->SetRightAligned(true);
AddScrollBar(bar);
}
CInput::~CInput()
{
}
void CInput::UpdateBufferPositionSetting()
{
GUI::SetSetting(this, "buffer_position", m_iBufferPos, true);
}
void CInput::ClearComposedText()
{
- CStrW* pCaption = nullptr;
- GUI::GetSettingPointer(this, "caption", pCaption);
-
- pCaption->erase(m_iInsertPos, m_iComposedLength);
+ CStrW& pCaption = GUI::GetSetting(this, "caption");
+ pCaption.erase(m_iInsertPos, m_iComposedLength);
m_iBufferPos = m_iInsertPos;
UpdateBufferPositionSetting();
m_iComposedLength = 0;
m_iComposedPos = 0;
}
InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
{
ENSURE(m_iBufferPos != -1);
switch (ev->ev.type)
{
case SDL_HOTKEYDOWN:
{
if (m_ComposingText)
return IN_HANDLED;
return ManuallyHandleHotkeyEvent(ev);
}
// SDL2 has a new method of text input that better supports Unicode and CJK
// see https://wiki.libsdl.org/Tutorials/TextInput
case SDL_TEXTINPUT:
{
if (m_Readonly)
return IN_PASS;
// Text has been committed, either single key presses or through an IME
- CStrW* pCaption = nullptr;
- GUI::GetSettingPointer(this, "caption", pCaption);
+ CStrW& pCaption = GUI::GetSetting(this, "caption");
std::wstring text = wstring_from_utf8(ev->ev.text.text);
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
if (m_ComposingText)
{
ClearComposedText();
m_ComposingText = false;
}
- if (m_iBufferPos == (int)pCaption->length())
- pCaption->append(text);
+ if (m_iBufferPos == static_cast(pCaption.length()))
+ pCaption.append(text);
else
- pCaption->insert(m_iBufferPos, text);
+ pCaption.insert(m_iBufferPos, text);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
m_iBufferPos += text.length();
UpdateBufferPositionSetting();
m_iBufferPos_Tail = -1;
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
return IN_HANDLED;
}
case SDL_TEXTEDITING:
{
if (m_Readonly)
return IN_PASS;
// Text is being composed with an IME
// TODO: indicate this by e.g. underlining the uncommitted text
- CStrW* pCaption = nullptr;
- GUI::GetSettingPointer(this, "caption", pCaption);
+ CStrW& pCaption = GUI::GetSetting(this, "caption");
const char* rawText = ev->ev.edit.text;
int rawLength = strlen(rawText);
std::wstring wtext = wstring_from_utf8(rawText);
debug_printf("SDL_TEXTEDITING: text=%s, start=%d, length=%d\n", rawText, ev->ev.edit.start, ev->ev.edit.length);
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
// Remember cursor position when text composition begins
if (!m_ComposingText)
m_iInsertPos = m_iBufferPos;
else
{
// Composed text is replaced each time
ClearComposedText();
}
m_ComposingText = ev->ev.edit.start != 0 || rawLength != 0;
if (m_ComposingText)
{
- pCaption->insert(m_iInsertPos, wtext);
+ pCaption.insert(m_iInsertPos, wtext);
// The text buffer is limited to SDL_TEXTEDITINGEVENT_TEXT_SIZE bytes, yet start
// increases without limit, so don't let it advance beyond the composed text length
m_iComposedLength = wtext.length();
m_iComposedPos = ev->ev.edit.start < m_iComposedLength ? ev->ev.edit.start : m_iComposedLength;
m_iBufferPos = m_iInsertPos + m_iComposedPos;
// TODO: composed text selection - what does ev.edit.length do?
m_iBufferPos_Tail = -1;
}
UpdateBufferPositionSetting();
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
return IN_HANDLED;
}
case SDL_KEYDOWN:
{
if (m_ComposingText)
return IN_HANDLED;
// Since the GUI framework doesn't handle to set settings
// in Unicode (CStrW), we'll simply retrieve the actual
// pointer and edit that.
- CStrW* pCaption = nullptr;
- GUI::GetSettingPointer(this, "caption", pCaption);
+ CStrW& pCaption = GUI::GetSetting(this, "caption");
SDL_Keycode keyCode = ev->ev.key.keysym.sym;
ManuallyImmutableHandleKeyDownEvent(keyCode, pCaption);
ManuallyMutableHandleKeyDownEvent(keyCode, pCaption);
UpdateBufferPositionSetting();
return IN_HANDLED;
}
default:
{
return IN_PASS;
}
}
}
-void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode, CStrW* pCaption)
+void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode, CStrW& pCaption)
{
if (m_Readonly)
return;
wchar_t cooked = 0;
switch (keyCode)
{
case SDLK_TAB:
{
SendEvent(GUIM_TAB, "tab");
// Don't send a textedit event, because it should only
// be sent if the GUI control changes the text
break;
}
case SDLK_BACKSPACE:
{
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
else
{
m_iBufferPos_Tail = -1;
- if (pCaption->empty() || m_iBufferPos == 0)
+ if (pCaption.empty() || m_iBufferPos == 0)
break;
- if (m_iBufferPos == (int)pCaption->length())
- *pCaption = pCaption->Left((long)pCaption->length() - 1);
+ if (m_iBufferPos == static_cast(pCaption.length()))
+ pCaption = pCaption.Left(static_cast(pCaption.length()) - 1);
else
- *pCaption = pCaption->Left(m_iBufferPos - 1) +
- pCaption->Right((long)pCaption->length() - m_iBufferPos);
+ pCaption =
+ pCaption.Left(m_iBufferPos - 1) +
+ pCaption.Right(static_cast(pCaption.length()) - m_iBufferPos);
--m_iBufferPos;
UpdateText(m_iBufferPos, m_iBufferPos + 1, m_iBufferPos);
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
break;
}
case SDLK_DELETE:
{
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
else
{
- if (pCaption->empty() || m_iBufferPos == (int)pCaption->length())
+ if (pCaption.empty() || m_iBufferPos == static_cast(pCaption.length()))
break;
- *pCaption = pCaption->Left(m_iBufferPos) +
- pCaption->Right((long)pCaption->length() - (m_iBufferPos + 1));
+ pCaption =
+ pCaption.Left(m_iBufferPos) +
+ pCaption.Right(static_cast(pCaption.length()) - (m_iBufferPos + 1));
UpdateText(m_iBufferPos, m_iBufferPos + 1, m_iBufferPos);
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
break;
}
case SDLK_KP_ENTER:
case SDLK_RETURN:
{
// 'Return' should do a Press event for single liners (e.g. submitting forms)
// otherwise a '\n' character will be added.
bool multiline;
GUI::GetSetting(this, "multiline", multiline);
if (!multiline)
{
SendEvent(GUIM_PRESSED, "press");
break;
}
cooked = '\n'; // Change to '\n' and do default:
FALLTHROUGH;
}
default: // Insert a character
{
// In SDL2, we no longer get Unicode wchars via SDL_Keysym
// we use text input events instead and they provide UTF-8 chars
if (cooked == 0)
return;
// check max length
int max_length;
GUI::GetSetting(this, "max_length", max_length);
- if (max_length != 0 && (int)pCaption->length() >= max_length)
+ if (max_length != 0 && static_cast(pCaption.length()) >= max_length)
break;
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
m_iBufferPos_Tail = -1;
- if (m_iBufferPos == (int)pCaption->length())
- *pCaption += cooked;
+ if (m_iBufferPos == static_cast(pCaption.length()))
+ pCaption += cooked;
else
- *pCaption = pCaption->Left(m_iBufferPos) + cooked +
- pCaption->Right((long)pCaption->length() - m_iBufferPos);
+ pCaption =
+ pCaption.Left(m_iBufferPos) + cooked +
+ pCaption.Right(static_cast(pCaption.length()) - m_iBufferPos);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos + 1);
++m_iBufferPos;
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
break;
}
}
}
-void CInput::ManuallyImmutableHandleKeyDownEvent(const SDL_Keycode keyCode, CStrW* pCaption)
+void CInput::ManuallyImmutableHandleKeyDownEvent(const SDL_Keycode keyCode, CStrW& pCaption)
{
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
switch (keyCode)
{
case SDLK_HOME:
{
// If there's not a selection, we should create one now
if (!shiftKeyPressed)
{
// Make sure a selection isn't created.
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
// Place tail at the current point:
m_iBufferPos_Tail = m_iBufferPos;
}
m_iBufferPos = 0;
m_WantedX = 0.0f;
UpdateAutoScroll();
break;
}
case SDLK_END:
{
// If there's not a selection, we should create one now
if (!shiftKeyPressed)
{
// Make sure a selection isn't created.
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
// Place tail at the current point:
m_iBufferPos_Tail = m_iBufferPos;
}
- m_iBufferPos = (long)pCaption->length();
+ m_iBufferPos = static_cast(pCaption.length());
m_WantedX = 0.0f;
UpdateAutoScroll();
break;
}
/**
* Conventions for Left/Right when text is selected:
*
* References:
*
* Visual Studio
* Visual Studio has the 'newer' approach, used by newer versions of
* things, and in newer applications. A left press will always place
* the pointer on the left edge of the selection, and then of course
* remove the selection. Right will do the exact same thing.
* If you have the pointer on the right edge and press right, it will
* in other words just remove the selection.
*
* Windows (eg. Notepad)
* A left press always takes the pointer a step to the left and
* removes the selection as if it were never there in the first place.
* Right of course does the same thing but to the right.
*
* I chose the Visual Studio convention. Used also in Word, gtk 2.0, MSN
* Messenger.
*/
case SDLK_LEFT:
{
m_WantedX = 0.f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
if (m_iBufferPos > 0)
--m_iBufferPos;
}
else
{
if (m_iBufferPos_Tail < m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
break;
}
case SDLK_RIGHT:
{
m_WantedX = 0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
- if (m_iBufferPos < (int)pCaption->length())
+ if (m_iBufferPos < static_cast(pCaption.length()))
++m_iBufferPos;
}
else
{
if (m_iBufferPos_Tail > m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
break;
}
/**
* Conventions for Up/Down when text is selected:
*
* References:
*
* Visual Studio
* Visual Studio has a very strange approach, down takes you below the
* selection to the next row, and up to the one prior to the whole
* selection. The weird part is that it is always aligned as the
* 'pointer'. I decided this is to much work for something that is
* a bit arbitrary
*
* Windows (eg. Notepad)
* Just like with left/right, the selection is destroyed and it moves
* just as if there never were a selection.
*
* I chose the Notepad convention even though I use the VS convention with
* left/right.
*/
case SDLK_UP:
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
std::list::iterator current = m_CharacterPositions.begin();
while (current != m_CharacterPositions.end())
{
if (m_iBufferPos >= current->m_ListStart &&
m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size())
break;
++current;
}
float pos_x;
if (m_iBufferPos - current->m_ListStart == 0)
pos_x = 0.f;
else
pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1];
if (m_WantedX > pos_x)
pos_x = m_WantedX;
// Now change row:
if (current != m_CharacterPositions.begin())
{
--current;
// Find X-position:
m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
}
// else we can't move up
UpdateAutoScroll();
break;
}
case SDLK_DOWN:
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
std::list::iterator current = m_CharacterPositions.begin();
while (current != m_CharacterPositions.end())
{
if (m_iBufferPos >= current->m_ListStart &&
m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size())
break;
++current;
}
float pos_x;
if (m_iBufferPos - current->m_ListStart == 0)
pos_x = 0.f;
else
pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1];
if (m_WantedX > pos_x)
pos_x = m_WantedX;
// Now change row:
// Add first, so we can check if it's .end()
++current;
if (current != m_CharacterPositions.end())
{
// Find X-position:
m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
}
// else we can't move up
UpdateAutoScroll();
break;
}
case SDLK_PAGEUP:
{
GetScrollBar(0).ScrollMinusPlenty();
UpdateAutoScroll();
break;
}
case SDLK_PAGEDOWN:
{
GetScrollBar(0).ScrollPlusPlenty();
UpdateAutoScroll();
break;
}
default:
{
break;
}
}
}
InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
{
- CStrW* pCaption = nullptr;
- GUI::GetSettingPointer(this, "caption", pCaption);
+ CStrW& pCaption = GUI::GetSetting(this, "caption");
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
std::string hotkey = static_cast(ev->ev.user.data1);
if (hotkey == "paste")
{
if (m_Readonly)
return IN_PASS;
m_WantedX = 0.0f;
wchar_t* text = sys_clipboard_get();
if (text)
{
if (SelectingText())
DeleteCurSelection();
- if (m_iBufferPos == (int)pCaption->length())
- *pCaption += text;
+ if (m_iBufferPos == static_cast(pCaption.length()))
+ pCaption += text;
else
- *pCaption = pCaption->Left(m_iBufferPos) + text +
- pCaption->Right((long) pCaption->length()-m_iBufferPos);
+ pCaption =
+ pCaption.Left(m_iBufferPos) + text +
+ pCaption.Right(static_cast(pCaption.length()) - m_iBufferPos);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
m_iBufferPos += (int)wcslen(text);
UpdateAutoScroll();
UpdateBufferPositionSetting();
sys_clipboard_free(text);
SendEvent(GUIM_TEXTEDIT, "textedit");
}
return IN_HANDLED;
}
else if (hotkey == "copy" || hotkey == "cut")
{
if (m_Readonly && hotkey == "cut")
return IN_PASS;
m_WantedX = 0.0f;
if (SelectingText())
{
int virtualFrom;
int virtualTo;
if (m_iBufferPos_Tail >= m_iBufferPos)
{
virtualFrom = m_iBufferPos;
virtualTo = m_iBufferPos_Tail;
}
else
{
virtualFrom = m_iBufferPos_Tail;
virtualTo = m_iBufferPos;
}
- CStrW text = (pCaption->Left(virtualTo)).Right(virtualTo - virtualFrom);
+ CStrW text = (pCaption.Left(virtualTo)).Right(virtualTo - virtualFrom);
sys_clipboard_set(&text[0]);
if (hotkey == "cut")
{
DeleteCurSelection();
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
}
}
return IN_HANDLED;
}
else if (hotkey == "text.delete.left")
{
if (m_Readonly)
return IN_PASS;
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
- if (!pCaption->empty() && m_iBufferPos != 0)
+ if (!pCaption.empty() && m_iBufferPos != 0)
{
m_iBufferPos_Tail = m_iBufferPos;
- CStrW searchString = pCaption->Left(m_iBufferPos);
+ CStrW searchString = pCaption.Left(m_iBufferPos);
// If we are starting in whitespace, adjust position until we get a non whitespace
while (m_iBufferPos > 0)
{
if (!iswspace(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
// If we end up on a punctuation char we just delete it (treat punct like a word)
if (iswpunct(searchString[m_iBufferPos - 1]))
m_iBufferPos--;
else
{
// Now we are on a non white space character, adjust position to char after next whitespace char is found
while (m_iBufferPos > 0)
{
if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
}
UpdateBufferPositionSetting();
DeleteCurSelection();
SendEvent(GUIM_TEXTEDIT, "textedit");
}
UpdateAutoScroll();
return IN_HANDLED;
}
else if (hotkey == "text.delete.right")
{
if (m_Readonly)
return IN_PASS;
m_WantedX = 0.0f;
if (SelectingText())
DeleteCurSelection();
- if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
+ if (!pCaption.empty() && m_iBufferPos < static_cast(pCaption.length()))
{
// Delete the word to the right of the cursor
m_iBufferPos_Tail = m_iBufferPos;
// Delete chars to the right unit we hit whitespace
- while (++m_iBufferPos < (int)pCaption->length())
+ while (++m_iBufferPos < static_cast(pCaption.length()))
{
- if (iswspace((*pCaption)[m_iBufferPos]) || iswpunct((*pCaption)[m_iBufferPos]))
+ if (iswspace(pCaption[m_iBufferPos]) || iswpunct(pCaption[m_iBufferPos]))
break;
}
// Eliminate any whitespace behind the word we just deleted
- while (m_iBufferPos < (int)pCaption->length())
+ while (m_iBufferPos < static_cast(pCaption.length()))
{
- if (!iswspace((*pCaption)[m_iBufferPos]))
+ if (!iswspace(pCaption[m_iBufferPos]))
break;
++m_iBufferPos;
}
UpdateBufferPositionSetting();
DeleteCurSelection();
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
return IN_HANDLED;
}
else if (hotkey == "text.move.left")
{
m_WantedX = 0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
- if (!pCaption->empty() && m_iBufferPos != 0)
+ if (!pCaption.empty() && m_iBufferPos != 0)
{
- CStrW searchString = pCaption->Left(m_iBufferPos);
+ CStrW searchString = pCaption.Left(m_iBufferPos);
// If we are starting in whitespace, adjust position until we get a non whitespace
while (m_iBufferPos > 0)
{
if (!iswspace(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
// If we end up on a puctuation char we just select it (treat punct like a word)
if (iswpunct(searchString[m_iBufferPos - 1]))
m_iBufferPos--;
else
{
// Now we are on a non white space character, adjust position to char after next whitespace char is found
while (m_iBufferPos > 0)
{
if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
}
}
}
else
{
if (m_iBufferPos_Tail < m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateBufferPositionSetting();
UpdateAutoScroll();
return IN_HANDLED;
}
else if (hotkey == "text.move.right")
{
m_WantedX = 0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
m_iBufferPos_Tail = -1;
else if (!SelectingText())
m_iBufferPos_Tail = m_iBufferPos;
- if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
+ if (!pCaption.empty() && m_iBufferPos < static_cast