Index: ps/trunk/source/gui/CChart.cpp =================================================================== --- ps/trunk/source/gui/CChart.cpp (revision 22760) +++ ps/trunk/source/gui/CChart.cpp (revision 22761) @@ -1,319 +1,317 @@ /* 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 DrawTriangleStrip(shader, GUI::GetSetting(this, "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() { 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) { 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())); 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"; + const CStrW& font = GUI::GetSetting(this, "font"); 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 22760) +++ ps/trunk/source/gui/CCheckBox.cpp (revision 22761) @@ -1,131 +1,126 @@ /* 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); const CGUIString& caption = GUI::GetSetting(this, "caption"); + const CStrW& font = GUI::GetSetting(this, "font"); 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); } 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() { 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 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/CList.cpp =================================================================== --- ps/trunk/source/gui/CList.cpp (revision 22760) +++ ps/trunk/source/gui/CList.cpp (revision 22761) @@ -1,511 +1,507 @@ /* 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 "CList.h" #include "gui/CGUIColor.h" #include "gui/CGUIScrollBarVertical.h" #include "lib/external_libraries/libsdl.h" #include "ps/CLogger.h" #include "ps/Profile.h" CList::CList(CGUI& pGUI) : IGUIObject(pGUI), IGUITextOwner(pGUI), IGUIScrollBarOwner(pGUI), m_Modified(false), m_PrevSelectedItem(-1), m_LastItemClickTime(0) { // Add sprite_disabled! TODO AddSetting("buffer_zone"); AddSetting("font"); AddSetting("scrollbar"); AddSetting("scrollbar_style"); AddSetting("sound_disabled"); AddSetting("sound_selected"); AddSetting("sprite"); AddSetting("sprite_selectarea"); AddSetting( "cell_id"); AddSetting("text_align"); AddSetting("textcolor"); AddSetting("textcolor_selected"); AddSetting( "selected"); // Index selected. -1 is none. AddSetting("auto_scroll"); AddSetting( "hovered"); AddSetting("tooltip"); AddSetting("tooltip_style"); // Each list item has both a name (in 'list') and an associated data string (in 'list_data') AddSetting("list"); AddSetting("list_data"); GUI::SetSetting(this, "scrollbar", false); GUI::SetSetting(this, "selected", -1); GUI::SetSetting(this, "hovered", -1); GUI::SetSetting(this, "auto_scroll", false); // Add scroll-bar CGUIScrollBarVertical* bar = new CGUIScrollBarVertical(pGUI); bar->SetRightAligned(true); AddScrollBar(bar); } CList::~CList() { } void CList::SetupText() { m_Modified = true; const CGUIList& pList = GUI::GetSetting(this, "list"); //ENSURE(m_GeneratedTexts.size()>=1); m_ItemsYPositions.resize(pList.m_Items.size() + 1); // Delete all generated texts. Some could probably be saved, // but this is easier, and this function will never be called // continuously, or even often, so it'll probably be okay. m_GeneratedTexts.clear(); - CStrW font; - if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) - // Use the default if none is specified - // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style. - font = L"default"; + const CStrW& font = GUI::GetSetting(this, "font"); bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); float width = GetListRect().GetWidth(); // remove scrollbar if applicable if (scrollbar && GetScrollBar(0).GetStyle()) width -= GetScrollBar(0).GetStyle()->m_Width; float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); // Generate texts float buffered_y = 0.f; for (size_t i = 0; i < pList.m_Items.size(); ++i) { CGUIText* text; if (!pList.m_Items[i].GetOriginalString().empty()) text = &AddText(pList.m_Items[i], font, width, buffer_zone, this); else { // Minimum height of a space character of the current font size CGUIString align_string; align_string.SetValue(L" "); text = &AddText(align_string, font, width, buffer_zone, this); } m_ItemsYPositions[i] = buffered_y; buffered_y += text->GetSize().cy; } m_ItemsYPositions[pList.m_Items.size()] = buffered_y; // Setup scrollbar if (scrollbar) { GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back()); GetScrollBar(0).SetScrollSpace(GetListRect().GetHeight()); CRect rect = GetListRect(); GetScrollBar(0).SetX(rect.right); GetScrollBar(0).SetY(rect.top); GetScrollBar(0).SetZ(GetBufferedZ()); GetScrollBar(0).SetLength(rect.bottom - rect.top); } } void CList::HandleMessage(SGUIMessage& Message) { IGUIScrollBarOwner::HandleMessage(Message); //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead! m_Modified = false; switch (Message.type) { case GUIM_SETTINGS_UPDATED: if (Message.value == "list") SetupText(); // If selected is changed, call "SelectionChange" if (Message.value == "selected") { // TODO: Check range bool auto_scroll; GUI::GetSetting(this, "auto_scroll", auto_scroll); if (auto_scroll) UpdateAutoScroll(); // TODO only works if lower-case, shouldn't it be made case sensitive instead? ScriptEvent("selectionchange"); } if (Message.value == "scrollbar") SetupText(); // Update scrollbar if (Message.value == "scrollbar_style") { CStr scrollbar_style; GUI::GetSetting(this, Message.value, scrollbar_style); GetScrollBar(0).SetScrollBarStyle(scrollbar_style); SetupText(); } break; case GUIM_MOUSE_PRESS_LEFT: { bool enabled; GUI::GetSetting(this, "enabled", enabled); if (!enabled) { PlaySound("sound_disabled"); break; } int hovered = GetHoveredItem(); if (hovered == -1) break; GUI::SetSetting(this, "selected", hovered); UpdateAutoScroll(); PlaySound("sound_selected"); if (timer_Time() - m_LastItemClickTime < SELECT_DBLCLICK_RATE && hovered == m_PrevSelectedItem) this->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, "mouseleftdoubleclickitem"); else this->SendEvent(GUIM_MOUSE_PRESS_LEFT_ITEM, "mouseleftclickitem"); m_LastItemClickTime = timer_Time(); m_PrevSelectedItem = hovered; break; } case GUIM_MOUSE_LEAVE: { int hoveredSetting = -1; GUI::GetSetting(this, "hovered", hoveredSetting); if (hoveredSetting == -1) break; GUI::SetSetting(this, "hovered", -1); ScriptEvent("hoverchange"); break; } case GUIM_MOUSE_OVER: { int hoveredSetting = -1; GUI::GetSetting(this, "hovered", hoveredSetting); int hovered = GetHoveredItem(); if (hovered == hoveredSetting) break; GUI::SetSetting(this, "hovered", hovered); ScriptEvent("hoverchange"); break; } case GUIM_LOAD: { CStr scrollbar_style; GUI::GetSetting(this, "scrollbar_style", scrollbar_style); GetScrollBar(0).SetScrollBarStyle(scrollbar_style); break; } default: break; } IGUITextOwner::HandleMessage(Message); } InReaction CList::ManuallyHandleEvent(const SDL_Event_* ev) { InReaction result = IN_PASS; if (ev->ev.type == SDL_KEYDOWN) { int szChar = ev->ev.key.keysym.sym; switch (szChar) { case SDLK_HOME: SelectFirstElement(); UpdateAutoScroll(); result = IN_HANDLED; break; case SDLK_END: SelectLastElement(); UpdateAutoScroll(); result = IN_HANDLED; break; case SDLK_UP: SelectPrevElement(); UpdateAutoScroll(); result = IN_HANDLED; break; case SDLK_DOWN: SelectNextElement(); UpdateAutoScroll(); result = IN_HANDLED; break; case SDLK_PAGEUP: GetScrollBar(0).ScrollMinusPlenty(); result = IN_HANDLED; break; case SDLK_PAGEDOWN: GetScrollBar(0).ScrollPlusPlenty(); result = IN_HANDLED; break; default: // Do nothing result = IN_PASS; } } return result; } void CList::Draw() { int selected; GUI::GetSetting(this, "selected", selected); DrawList(selected, "sprite", "sprite_selectarea", "textcolor"); } void CList::DrawList(const int& selected, const CStr& _sprite, const CStr& _sprite_selected, const CStr& _textcolor) { float bz = GetBufferedZ(); // First call draw on ScrollBarOwner bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); if (scrollbar) IGUIScrollBarOwner::Draw(); { CRect rect = GetListRect(); CGUISpriteInstance& sprite = GUI::GetSetting(this, _sprite); CGUISpriteInstance& sprite_selectarea = GUI::GetSetting(this, _sprite_selected); const int cell_id = GUI::GetSetting(this, "cell_id"); m_pGUI.DrawSprite(sprite, cell_id, bz, rect); float scroll = 0.f; if (scrollbar) scroll = GetScrollBar(0).GetPos(); if (selected >= 0 && selected+1 < (int)m_ItemsYPositions.size()) { // Get rectangle of selection: CRect rect_sel(rect.left, rect.top + m_ItemsYPositions[selected] - scroll, rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll); if (rect_sel.top <= rect.bottom && rect_sel.bottom >= rect.top) { if (rect_sel.bottom > rect.bottom) rect_sel.bottom = rect.bottom; if (rect_sel.top < rect.top) rect_sel.top = rect.top; if (scrollbar) { // Remove any overlapping area of the scrollbar. if (rect_sel.right > GetScrollBar(0).GetOuterRect().left && rect_sel.right <= GetScrollBar(0).GetOuterRect().right) rect_sel.right = GetScrollBar(0).GetOuterRect().left; if (rect_sel.left >= GetScrollBar(0).GetOuterRect().left && rect_sel.left < GetScrollBar(0).GetOuterRect().right) rect_sel.left = GetScrollBar(0).GetOuterRect().right; } m_pGUI.DrawSprite(sprite_selectarea, cell_id, bz+0.05f, rect_sel); } } const CGUIList& pList = GUI::GetSetting(this, "list"); const CGUIColor& color = GUI::GetSetting(this, _textcolor); for (size_t i = 0; i < pList.m_Items.size(); ++i) { if (m_ItemsYPositions[i+1] - scroll < 0 || m_ItemsYPositions[i] - scroll > rect.GetHeight()) continue; // Clipping area (we'll have to substract the scrollbar) CRect cliparea = GetListRect(); if (scrollbar) { if (cliparea.right > GetScrollBar(0).GetOuterRect().left && cliparea.right <= GetScrollBar(0).GetOuterRect().right) cliparea.right = GetScrollBar(0).GetOuterRect().left; if (cliparea.left >= GetScrollBar(0).GetOuterRect().left && cliparea.left < GetScrollBar(0).GetOuterRect().right) cliparea.left = GetScrollBar(0).GetOuterRect().right; } DrawText(i, color, rect.TopLeft() - CPos(0.f, scroll - m_ItemsYPositions[i]), bz + 0.1f, cliparea); } } } void CList::AddItem(const CStrW& str, const CStrW& data) { CGUIString gui_string; gui_string.SetValue(str); // Do not send a settings-changed message CGUIList& pList = GUI::GetSetting(this, "list"); pList.m_Items.push_back(gui_string); CGUIString data_string; data_string.SetValue(data); CGUIList& pListData = GUI::GetSetting(this, "list_data"); pListData.m_Items.push_back(data_string); // TODO Temp SetupText(); } bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile) { int elmt_item = pFile->GetElementID("item"); if (child.GetNodeName() == elmt_item) { AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8()); return true; } return false; } void CList::SelectNextElement() { int selected = GUI::GetSetting(this, "selected"); const CGUIList& pList = GUI::GetSetting(this, "list"); if (selected != static_cast(pList.m_Items.size()) - 1) { ++selected; GUI::SetSetting(this, "selected", selected); PlaySound("sound_selected"); } } void CList::SelectPrevElement() { int selected = GUI::GetSetting(this, "selected"); if (selected > 0) { --selected; GUI::SetSetting(this, "selected", selected); PlaySound("sound_selected"); } } void CList::SelectFirstElement() { if (GUI::GetSetting(this, "selected") >= 0) GUI::SetSetting(this, "selected", 0); } void CList::SelectLastElement() { const CGUIList& pList = GUI::GetSetting(this, "list"); const int index = static_cast(pList.m_Items.size()) - 1; if (GUI::GetSetting(this, "selected") != index) GUI::SetSetting(this, "selected", index); } void CList::UpdateAutoScroll() { const int selected = GUI::GetSetting(this, "selected"); const bool scrollbar = GUI::GetSetting(this, "scrollbar"); // No scrollbar, no scrolling (at least it's not made to work properly). if (!scrollbar || selected < 0 || static_cast(selected) >= m_ItemsYPositions.size()) return; float scroll = GetScrollBar(0).GetPos(); // Check upper boundary if (m_ItemsYPositions[selected] < scroll) { GetScrollBar(0).SetPos(m_ItemsYPositions[selected]); return; // this means, if it wants to align both up and down at the same time // this will have precedence. } // Check lower boundary CRect rect = GetListRect(); if (m_ItemsYPositions[selected+1]-rect.GetHeight() > scroll) GetScrollBar(0).SetPos(m_ItemsYPositions[selected+1]-rect.GetHeight()); } int CList::GetHoveredItem() { const bool scrollbar = GUI::GetSetting(this, "scrollbar"); const float scroll = scrollbar ? GetScrollBar(0).GetPos() : 0.f; const CRect& rect = GetListRect(); CPos mouse = m_pGUI.GetMousePos(); mouse.y += scroll; // Mouse is over scrollbar if (scrollbar && GetScrollBar(0).IsVisible() && mouse.x >= GetScrollBar(0).GetOuterRect().left && mouse.x <= GetScrollBar(0).GetOuterRect().right) return -1; const CGUIList& pList = GUI::GetSetting(this, "list"); for (size_t i = 0; i < pList.m_Items.size(); ++i) if (mouse.y >= rect.top + m_ItemsYPositions[i] && mouse.y < rect.top + m_ItemsYPositions[i + 1]) return i; return -1; } Index: ps/trunk/source/gui/COList.cpp =================================================================== --- ps/trunk/source/gui/COList.cpp (revision 22760) +++ ps/trunk/source/gui/COList.cpp (revision 22761) @@ -1,466 +1,462 @@ /* 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 "COList.h" #include "gui/CGUIColor.h" #include "i18n/L10n.h" #include "ps/CLogger.h" const float SORT_SPRITE_DIM = 16.0f; const CPos COLUMN_SHIFT = CPos(0, 4); COList::COList(CGUI& pGUI) : CList(pGUI), IGUIObject(pGUI) { AddSetting("sprite_heading"); AddSetting("sortable"); // The actual sorting is done in JS for more versatility AddSetting("selected_column"); AddSetting("selected_column_order"); AddSetting("sprite_asc"); // Show the order of sorting AddSetting("sprite_desc"); AddSetting("sprite_not_sorted"); } void COList::SetupText() { const CGUIList& pList = GUI::GetSetting(this, "list"); m_ItemsYPositions.resize(pList.m_Items.size() + 1); // Delete all generated texts. Some could probably be saved, // but this is easier, and this function will never be called // continuously, or even often, so it'll probably be okay. m_GeneratedTexts.clear(); - CStrW font; - if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) - // Use the default if none is specified - // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style. - font = L"default"; + const CStrW& font = GUI::GetSetting(this, "font"); bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); m_TotalAvailableColumnWidth = GetListRect().GetWidth(); // remove scrollbar if applicable if (scrollbar && GetScrollBar(0).GetStyle()) m_TotalAvailableColumnWidth -= GetScrollBar(0).GetStyle()->m_Width; float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); m_HeadingHeight = SORT_SPRITE_DIM; // At least the size of the sorting sprite for (const COListColumn& column : m_Columns) { float width = column.m_Width; if (column.m_Width > 0 && column.m_Width < 1) width *= m_TotalAvailableColumnWidth; CGUIString gui_string; gui_string.SetValue(column.m_Heading); const CGUIText& text = AddText(gui_string, font, width, buffer_zone, this); m_HeadingHeight = std::max(m_HeadingHeight, text.GetSize().cy + COLUMN_SHIFT.y); } // Generate texts float buffered_y = 0.f; for (size_t i = 0; i < pList.m_Items.size(); ++i) { m_ItemsYPositions[i] = buffered_y; float shift = 0.0f; for (const COListColumn& column : m_Columns) { float width = column.m_Width; if (column.m_Width > 0 && column.m_Width < 1) width *= m_TotalAvailableColumnWidth; CGUIList& pList_c = GUI::GetSetting(this, "list_" + column.m_Id); CGUIText* text; if (!pList_c.m_Items[i].GetOriginalString().empty()) text = &AddText(pList_c.m_Items[i], font, width, buffer_zone, this); else { // Minimum height of a space character of the current font size CGUIString align_string; align_string.SetValue(L" "); text = &AddText(align_string, font, width, buffer_zone, this); } shift = std::max(shift, text->GetSize().cy); } buffered_y += shift; } m_ItemsYPositions[pList.m_Items.size()] = buffered_y; if (scrollbar) { CRect rect = GetListRect(); GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back()); GetScrollBar(0).SetScrollSpace(rect.GetHeight()); GetScrollBar(0).SetX(rect.right); GetScrollBar(0).SetY(rect.top); GetScrollBar(0).SetZ(GetBufferedZ()); GetScrollBar(0).SetLength(rect.bottom - rect.top); } } CRect COList::GetListRect() const { return m_CachedActualSize + CRect(0, m_HeadingHeight, 0, 0); } void COList::HandleMessage(SGUIMessage& Message) { CList::HandleMessage(Message); switch (Message.type) { // If somebody clicks on the column heading case GUIM_MOUSE_PRESS_LEFT: { bool sortable; GUI::GetSetting(this, "sortable", sortable); if (!sortable) return; const CPos& mouse = m_pGUI.GetMousePos(); if (!m_CachedActualSize.PointInside(mouse)) return; CStr selectedColumn; GUI::GetSetting(this, "selected_column", selectedColumn); int selectedColumnOrder; GUI::GetSetting(this, "selected_column_order", selectedColumnOrder); float xpos = 0; for (const COListColumn& column : m_Columns) { bool hidden = false; GUI::GetSetting(this, "hidden_" + column.m_Id, hidden); if (hidden) continue; float width = column.m_Width; // Check if it's a decimal value, and if so, assume relative positioning. if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 0); if (mouse.x >= leftTopCorner.x && mouse.x < leftTopCorner.x + width && mouse.y < leftTopCorner.y + m_HeadingHeight) { if (column.m_Id != selectedColumn) { selectedColumnOrder = 1; selectedColumn = column.m_Id; } else selectedColumnOrder = -selectedColumnOrder; GUI::SetSetting(this, "selected_column", column.m_Id); GUI::SetSetting(this, "selected_column_order", selectedColumnOrder); ScriptEvent("selectioncolumnchange"); PlaySound("sound_selected"); return; } xpos += width; } return; } default: return; } } bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile) { #define ELMT(x) int elmt_##x = pFile->GetElementID(#x) #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) ELMT(item); ELMT(column); ELMT(translatableAttribute); ATTR(id); ATTR(context); if (child.GetNodeName() == elmt_item) { AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8()); return true; } else if (child.GetNodeName() == elmt_column) { COListColumn column; bool hidden = false; for (XMBAttribute attr : child.GetAttributes()) { CStr attr_name(pFile->GetAttributeString(attr.Name)); CStr attr_value(attr.Value); if (attr_name == "color") { if (!GUI::ParseString(&m_pGUI, attr_value.FromUTF8(), column.m_TextColor)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.c_str(), attr_value.c_str()); } else if (attr_name == "id") { column.m_Id = attr_value; } else if (attr_name == "hidden") { if (!GUI::ParseString(&m_pGUI, attr_value.FromUTF8(), hidden)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.c_str(), attr_value.c_str()); } else if (attr_name == "width") { float width; if (!GUI::ParseString(&m_pGUI, attr_value.FromUTF8(), width)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.c_str(), attr_value.c_str()); else { // Check if it's a relative value, and save as decimal if so. if (attr_value.find("%") != std::string::npos) width = width / 100.f; column.m_Width = width; } } else if (attr_name == "heading") { column.m_Heading = attr_value.FromUTF8(); } } for (XMBElement grandchild : child.GetChildNodes()) { if (grandchild.GetNodeName() != elmt_translatableAttribute) continue; CStr attributeName(grandchild.GetAttributes().GetNamedItem(attr_id)); // only the heading is translatable for list column if (attributeName.empty() || attributeName != "heading") { LOGERROR("GUI: translatable attribute in olist column that isn't a heading. (object: %s)", this->GetPresentableName().c_str()); continue; } CStr value(grandchild.GetText()); if (value.empty()) continue; CStr context(grandchild.GetAttributes().GetNamedItem(attr_context)); // Read the context if any. if (!context.empty()) { CStr translatedValue(g_L10n.TranslateWithContext(context, value)); column.m_Heading = translatedValue.FromUTF8(); } else { CStr translatedValue(g_L10n.Translate(value)); column.m_Heading = translatedValue.FromUTF8(); } } AddSetting("list_" + column.m_Id); AddSetting("hidden_" + column.m_Id); GUI::SetSetting(this, "hidden_" + column.m_Id, hidden); m_Columns.emplace_back(std::move(column)); SetupText(); return true; } return false; } void COList::DrawList(const int& selected, const CStr& _sprite, const CStr& _sprite_selected, const CStr& _textcolor) { float bz = GetBufferedZ(); bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); if (scrollbar) IGUIScrollBarOwner::Draw(); CRect rect = GetListRect(); CGUISpriteInstance& sprite = GUI::GetSetting(this, _sprite); CGUISpriteInstance& sprite_selectarea = GUI::GetSetting(this, _sprite_selected); int cell_id; GUI::GetSetting(this, "cell_id", cell_id); m_pGUI.DrawSprite(sprite, cell_id, bz, rect); float scroll = 0.f; if (scrollbar) scroll = GetScrollBar(0).GetPos(); // Draw item selection if (selected != -1) { ENSURE(selected >= 0 && selected+1 < (int)m_ItemsYPositions.size()); // Get rectangle of selection: CRect rect_sel(rect.left, rect.top + m_ItemsYPositions[selected] - scroll, rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll); if (rect_sel.top <= rect.bottom && rect_sel.bottom >= rect.top) { if (rect_sel.bottom > rect.bottom) rect_sel.bottom = rect.bottom; if (rect_sel.top < rect.top) rect_sel.top = rect.top; if (scrollbar) { // Remove any overlapping area of the scrollbar. if (rect_sel.right > GetScrollBar(0).GetOuterRect().left && rect_sel.right <= GetScrollBar(0).GetOuterRect().right) rect_sel.right = GetScrollBar(0).GetOuterRect().left; if (rect_sel.left >= GetScrollBar(0).GetOuterRect().left && rect_sel.left < GetScrollBar(0).GetOuterRect().right) rect_sel.left = GetScrollBar(0).GetOuterRect().right; } // Draw item selection m_pGUI.DrawSprite(sprite_selectarea, cell_id, bz+0.05f, rect_sel); } } // Draw line above column header CGUISpriteInstance& sprite_heading = GUI::GetSetting(this, "sprite_heading"); CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right, m_CachedActualSize.top + m_HeadingHeight); m_pGUI.DrawSprite(sprite_heading, cell_id, bz, rect_head); // Draw column headers bool sortable; GUI::GetSetting(this, "sortable", sortable); CStr selectedColumn; GUI::GetSetting(this, "selected_column", selectedColumn); int selectedColumnOrder; GUI::GetSetting(this, "selected_column_order", selectedColumnOrder); const CGUIColor& color = GUI::GetSetting(this, _textcolor); float xpos = 0; for (size_t col = 0; col < m_Columns.size(); ++col) { bool hidden = false; GUI::GetSetting(this, "hidden_" + m_Columns[col].m_Id, hidden); if (hidden) continue; // Check if it's a decimal value, and if so, assume relative positioning. float width = m_Columns[col].m_Width; if (m_Columns[col].m_Width < 1 && m_Columns[col].m_Width > 0) width *= m_TotalAvailableColumnWidth; CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 0); // Draw sort arrows in colum header if (sortable) { CStr spriteName; if (selectedColumn == m_Columns[col].m_Id) { if (selectedColumnOrder == 0) LOGERROR("selected_column_order must not be 0"); if (selectedColumnOrder != -1) spriteName = "sprite_asc"; else spriteName = "sprite_desc"; } else spriteName = "sprite_not_sorted"; CGUISpriteInstance& sprite = GUI::GetSetting(this, spriteName); m_pGUI.DrawSprite(sprite, cell_id, bz + 0.1f, CRect(leftTopCorner + CPos(width - SORT_SPRITE_DIM, 0), leftTopCorner + CPos(width, SORT_SPRITE_DIM))); } // Draw column header text DrawText(col, color, leftTopCorner + COLUMN_SHIFT, bz + 0.1f, rect_head); xpos += width; } // Draw list items for each column const CGUIList& pList = GUI::GetSetting(this, "list"); const size_t objectsCount = m_Columns.size(); for (size_t i = 0; i < pList.m_Items.size(); ++i) { if (m_ItemsYPositions[i+1] - scroll < 0 || m_ItemsYPositions[i] - scroll > rect.GetHeight()) continue; const float rowHeight = m_ItemsYPositions[i+1] - m_ItemsYPositions[i]; // Clipping area (we'll have to substract the scrollbar) CRect cliparea = GetListRect(); if (scrollbar) { if (cliparea.right > GetScrollBar(0).GetOuterRect().left && cliparea.right <= GetScrollBar(0).GetOuterRect().right) cliparea.right = GetScrollBar(0).GetOuterRect().left; if (cliparea.left >= GetScrollBar(0).GetOuterRect().left && cliparea.left < GetScrollBar(0).GetOuterRect().right) cliparea.left = GetScrollBar(0).GetOuterRect().right; } // Draw all items for that column xpos = 0; for (size_t col = 0; col < objectsCount; ++col) { bool hidden = false; GUI::GetSetting(this, "hidden_" + m_Columns[col].m_Id, hidden); if (hidden) continue; // Determine text position and width const CPos textPos = rect.TopLeft() + CPos(xpos, -scroll + m_ItemsYPositions[i]); float width = m_Columns[col].m_Width; // Check if it's a decimal value, and if so, assume relative positioning. if (m_Columns[col].m_Width < 1 && m_Columns[col].m_Width > 0) width *= m_TotalAvailableColumnWidth; // Clip text to the column (to prevent drawing text into the neighboring column) CRect cliparea2 = cliparea; cliparea2.right = std::min(cliparea2.right, textPos.x + width); cliparea2.bottom = std::min(cliparea2.bottom, textPos.y + rowHeight); // Draw list item DrawText(objectsCount * (i +/*Heading*/1) + col, m_Columns[col].m_TextColor, textPos, bz + 0.1f, cliparea2); xpos += width; } } } Index: ps/trunk/source/gui/CText.cpp =================================================================== --- ps/trunk/source/gui/CText.cpp (revision 22760) +++ ps/trunk/source/gui/CText.cpp (revision 22761) @@ -1,261 +1,256 @@ /* 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 "CText.h" #include "gui/CGUIScrollBarVertical.h" #include "gui/GUI.h" #include "lib/ogl.h" CText::CText(CGUI& pGUI) : IGUIObject(pGUI), IGUIScrollBarOwner(pGUI), IGUITextOwner(pGUI) { AddSetting("buffer_zone"); AddSetting("caption"); AddSetting("cell_id"); AddSetting("clip"); AddSetting("font"); AddSetting("scrollbar"); AddSetting("scrollbar_style"); AddSetting("scroll_bottom"); AddSetting("scroll_top"); AddSetting("sprite"); AddSetting("text_align"); AddSetting("text_valign"); AddSetting("textcolor"); AddSetting("textcolor_disabled"); AddSetting("tooltip"); AddSetting("tooltip_style"); // Private settings AddSetting("_icon_tooltip"); AddSetting("_icon_tooltip_style"); //GUI::SetSetting(this, "ghost", true); GUI::SetSetting(this, "scrollbar", false); GUI::SetSetting(this, "clip", true); // Add scroll-bar CGUIScrollBarVertical* bar = new CGUIScrollBarVertical(pGUI); bar->SetRightAligned(true); AddScrollBar(bar); // Add text AddText(); } CText::~CText() { } void CText::SetupText() { if (m_GeneratedTexts.empty()) return; - CStrW font; - if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) - // Use the default if none is specified - // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style. - font = L"default"; - bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); float width = m_CachedActualSize.GetWidth(); // remove scrollbar if applicable if (scrollbar && GetScrollBar(0).GetStyle()) width -= GetScrollBar(0).GetStyle()->m_Width; const CGUIString& caption = GUI::GetSetting(this, "caption"); + const CStrW& font = GUI::GetSetting(this, "font"); const float buffer_zone = GUI::GetSetting(this, "buffer_zone"); m_GeneratedTexts[0] = CGUIText(m_pGUI, caption, font, width, buffer_zone, this); if (!scrollbar) CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); // Setup scrollbar if (scrollbar) { bool scroll_top = false, scroll_bottom = false; GUI::GetSetting(this, "scroll_bottom", scroll_bottom); GUI::GetSetting(this, "scroll_top", scroll_top); // If we are currently scrolled to the bottom of the text, // then add more lines of text, update the scrollbar so we // stick to the bottom. // (Use 1.5px delta so this triggers the first time caption is set) bool bottom = false; if (scroll_bottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f) bottom = true; GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0].GetSize().cy); GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); GetScrollBar(0).SetX(m_CachedActualSize.right); GetScrollBar(0).SetY(m_CachedActualSize.top); GetScrollBar(0).SetZ(GetBufferedZ()); GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); if (bottom) GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos()); if (scroll_top) GetScrollBar(0).SetPos(0.0f); } } void CText::HandleMessage(SGUIMessage& Message) { IGUIScrollBarOwner::HandleMessage(Message); //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead! switch (Message.type) { case GUIM_SETTINGS_UPDATED: if (Message.value == "scrollbar") SetupText(); // Update scrollbar if (Message.value == "scrollbar_style") { CStr scrollbar_style; GUI::GetSetting(this, Message.value, scrollbar_style); GetScrollBar(0).SetScrollBarStyle(scrollbar_style); SetupText(); } break; case GUIM_MOUSE_WHEEL_DOWN: { GetScrollBar(0).ScrollPlus(); // Since the scroll was changed, let's simulate a mouse movement // to check if scrollbar now is hovered SGUIMessage msg(GUIM_MOUSE_MOTION); HandleMessage(msg); break; } case GUIM_MOUSE_WHEEL_UP: { GetScrollBar(0).ScrollMinus(); // Since the scroll was changed, let's simulate a mouse movement // to check if scrollbar now is hovered SGUIMessage msg(GUIM_MOUSE_MOTION); HandleMessage(msg); break; } case GUIM_LOAD: { GetScrollBar(0).SetX(m_CachedActualSize.right); GetScrollBar(0).SetY(m_CachedActualSize.top); GetScrollBar(0).SetZ(GetBufferedZ()); GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top); CStr scrollbar_style; GUI::GetSetting(this, "scrollbar_style", scrollbar_style); GetScrollBar(0).SetScrollBarStyle(scrollbar_style); break; } default: break; } IGUITextOwner::HandleMessage(Message); } void CText::Draw() { float bz = GetBufferedZ(); // First call draw on ScrollBarOwner bool scrollbar; GUI::GetSetting(this, "scrollbar", scrollbar); if (scrollbar) // Draw scrollbar IGUIScrollBarOwner::Draw(); CGUISpriteInstance& sprite = GUI::GetSetting(this, "sprite"); int cell_id; bool clip; GUI::GetSetting(this, "cell_id", cell_id); GUI::GetSetting(this, "clip", clip); m_pGUI.DrawSprite(sprite, cell_id, bz, m_CachedActualSize); float scroll = 0.f; if (scrollbar) scroll = GetScrollBar(0).GetPos(); // Clipping area (we'll have to subtract the scrollbar) CRect cliparea; if (clip) { cliparea = m_CachedActualSize; if (scrollbar) { // subtract scrollbar from cliparea if (cliparea.right > GetScrollBar(0).GetOuterRect().left && cliparea.right <= GetScrollBar(0).GetOuterRect().right) cliparea.right = GetScrollBar(0).GetOuterRect().left; if (cliparea.left >= GetScrollBar(0).GetOuterRect().left && cliparea.left < GetScrollBar(0).GetOuterRect().right) cliparea.left = GetScrollBar(0).GetOuterRect().right; } } bool enabled; GUI::GetSetting(this, "enabled", enabled); const CGUIColor& color = GUI::GetSetting(this, enabled ? "textcolor" : "textcolor_disabled"); if (scrollbar) DrawText(0, color, m_CachedActualSize.TopLeft() - CPos(0.f, scroll), bz + 0.1f, cliparea); else DrawText(0, color, m_TextPos, bz + 0.1f, cliparea); } bool CText::MouseOverIcon() { for (const CGUIText& guitext : m_GeneratedTexts) for (const CGUIText::SSpriteCall& spritecall : guitext.GetSpriteCalls()) { // Check mouse over sprite if (!spritecall.m_Area.PointInside(m_pGUI.GetMousePos() - m_CachedActualSize.TopLeft())) continue; // If tooltip exists, set the property if (!spritecall.m_Tooltip.empty()) { SetSetting("_icon_tooltip_style", spritecall.m_TooltipStyle); SetSetting("_icon_tooltip", spritecall.m_Tooltip); } return true; } return false; } Index: ps/trunk/source/gui/CTooltip.cpp =================================================================== --- ps/trunk/source/gui/CTooltip.cpp (revision 22760) +++ ps/trunk/source/gui/CTooltip.cpp (revision 22761) @@ -1,165 +1,162 @@ /* 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 "CTooltip.h" #include "CGUI.h" #include CTooltip::CTooltip(CGUI& pGUI) : IGUIObject(pGUI), IGUITextOwner(pGUI) { // If the tooltip is an object by itself: AddSetting("buffer_zone"); AddSetting("caption"); AddSetting("font"); AddSetting("sprite"); AddSetting("delay"); AddSetting("textcolor"); AddSetting("maxwidth"); AddSetting("offset"); AddSetting("anchor"); AddSetting("text_align"); // This is used for tooltips that are hidden/revealed manually by scripts, rather than through the standard tooltip display mechanism AddSetting("independent"); // If the tooltip is just a reference to another object: AddSetting("use_object"); AddSetting("hide_object"); // Private settings: // This is set by GUITooltip AddSetting("_mousepos"); // Defaults GUI::SetSetting(this, "delay", 500); GUI::SetSetting(this, "anchor", EVAlign_Bottom); GUI::SetSetting(this, "text_align", EAlign_Left); // Set up a blank piece of text, to be replaced with a more // interesting message later AddText(); } CTooltip::~CTooltip() { } void CTooltip::SetupText() { ENSURE(m_GeneratedTexts.size() == 1); - 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); const CGUIString& caption = GUI::GetSetting(this, "caption"); + const CStrW& font = GUI::GetSetting(this, "font"); float max_width = 0.f; GUI::GetSetting(this, "maxwidth", max_width); m_GeneratedTexts[0] = CGUIText(m_pGUI, caption, font, max_width, buffer_zone, this); // Position the tooltip relative to the mouse: CPos mousepos, offset; EVAlign anchor; bool independent; GUI::GetSetting(this, "independent", independent); if (independent) mousepos = m_pGUI.GetMousePos(); else GUI::GetSetting(this, "_mousepos", mousepos); GUI::GetSetting(this, "offset", offset); GUI::GetSetting(this, "anchor", anchor); float textwidth = m_GeneratedTexts[0].GetSize().cx; float textheight = m_GeneratedTexts[0].GetSize().cy; CClientArea size; size.pixel.left = mousepos.x + offset.x; size.pixel.right = size.pixel.left + textwidth; switch (anchor) { case EVAlign_Top: size.pixel.top = mousepos.y + offset.y; size.pixel.bottom = size.pixel.top + textheight; break; case EVAlign_Bottom: size.pixel.bottom = mousepos.y + offset.y; size.pixel.top = size.pixel.bottom - textheight; break; case EVAlign_Center: size.pixel.top = mousepos.y + offset.y - textheight/2.f; size.pixel.bottom = size.pixel.top + textwidth; break; default: debug_warn(L"Invalid EVAlign!"); } // Reposition the tooltip if it's falling off the screen: extern int g_xres, g_yres; extern float g_GuiScale; float screenw = g_xres / g_GuiScale; float screenh = g_yres / g_GuiScale; if (size.pixel.top < 0.f) size.pixel.bottom -= size.pixel.top, size.pixel.top = 0.f; else if (size.pixel.bottom > screenh) size.pixel.top -= (size.pixel.bottom-screenh), size.pixel.bottom = screenh; if (size.pixel.left < 0.f) size.pixel.right -= size.pixel.left, size.pixel.left = 0.f; else if (size.pixel.right > screenw) size.pixel.left -= (size.pixel.right-screenw), size.pixel.right = screenw; GUI::SetSetting(this, "size", size); } void CTooltip::HandleMessage(SGUIMessage& Message) { IGUITextOwner::HandleMessage(Message); } void CTooltip::Draw() { float z = 900.f; // TODO: Find a nicer way of putting the tooltip on top of everything else CGUISpriteInstance& sprite = GUI::GetSetting(this, "sprite"); // Normally IGUITextOwner will handle this updating but since SetupText can modify the position // we need to call it now *before* we do the rest of the drawing if (!m_GeneratedTextsValid) { SetupText(); m_GeneratedTextsValid = true; } m_pGUI.DrawSprite(sprite, 0, z, m_CachedActualSize); const CGUIColor& color = GUI::GetSetting(this, "textcolor"); DrawText(0, color, m_CachedActualSize.TopLeft(), z+0.1f); }