#include <queue>
#include <sstream>
#include <utility>
-#include <string.h>
+#include <cstring>
#include <IGUISkin.h>
#include <IGUIFont.h>
#include <IGUIScrollBar.h>
+#include "client/renderingengine.h"
#include "debug.h"
#include "log.h"
-#include "tile.h"
+#include "client/tile.h"
#include "gettime.h"
#include "util/string.h"
#include "util/numeric.h"
-#include "guiFormSpecMenu.h" // for parseColor()
+#include "util/string.h" // for parseColorString()
+#include "settings.h" // for settings
+#include "porting.h" // for dpi
+#include "guiscalingfilter.h"
/*
GUITable
ISimpleTextureSource *tsrc
):
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
- m_tsrc(tsrc),
- m_is_textlist(false),
- m_has_tree_column(false),
- m_selected(-1),
- m_sel_column(0),
- m_sel_doubleclick(false),
- m_keynav_time(0),
- m_keynav_buffer(L""),
- m_border(true),
- m_color(255, 255, 255, 255),
- m_background(255, 0, 0, 0),
- m_highlight(255, 70, 100, 50),
- m_highlight_text(255, 255, 255, 255),
- m_rowheight(1),
- m_font(NULL),
- m_scrollbar(NULL)
+ m_tsrc(tsrc)
{
assert(tsrc != NULL);
setTabStop(true);
setTabOrder(-1);
updateAbsolutePosition();
+
+ core::rect<s32> relative_rect = m_scrollbar->getRelativePosition();
+ s32 width = (relative_rect.getWidth()/(2.0/3.0)) *
+ RenderingEngine::getDisplayDensity() *
+ g_settings->getFloat("gui_scaling");
+ m_scrollbar->setRelativePosition(core::rect<s32>(
+ relative_rect.LowerRightCorner.X-width,relative_rect.UpperLeftCorner.Y,
+ relative_rect.LowerRightCorner.X,relative_rect.LowerRightCorner.Y
+ ));
}
GUITable::~GUITable()
{
- for (size_t i = 0; i < m_rows.size(); ++i)
- delete[] m_rows[i].cells;
+ for (GUITable::Row &row : m_rows)
+ delete[] row.cells;
if (m_font)
m_font->drop();
-
+
m_scrollbar->remove();
}
size_t equal_pos = str.find('=');
if (equal_pos == std::string::npos)
return GUITable::Option(str, "");
- else
- return GUITable::Option(str.substr(0, equal_pos),
- str.substr(equal_pos + 1));
+
+ return GUITable::Option(str.substr(0, equal_pos),
+ str.substr(equal_pos + 1));
}
void GUITable::setTextList(const std::vector<std::string> &content,
cell->content_index = allocString(s.substr(2));
}
else if (s[0] == '#' && s.size() >= 7 &&
- GUIFormSpecMenu::parseColor(
+ parseColorString(
s.substr(0,7), cell->color, false)) {
// single # for color
cell->color_defined = true;
// Handle table options
video::SColor default_color(255, 255, 255, 255);
s32 opendepth = 0;
- for (size_t k = 0; k < options.size(); ++k) {
- const std::string &name = options[k].name;
- const std::string &value = options[k].value;
+ for (const Option &option : options) {
+ const std::string &name = option.name;
+ const std::string &value = option.value;
if (name == "color")
- GUIFormSpecMenu::parseColor(value, m_color, false);
+ parseColorString(value, m_color, false);
else if (name == "background")
- GUIFormSpecMenu::parseColor(value, m_background, false);
+ parseColorString(value, m_background, false);
else if (name == "border")
m_border = is_yes(value);
else if (name == "highlight")
- GUIFormSpecMenu::parseColor(value, m_highlight, false);
+ parseColorString(value, m_highlight, false);
else if (name == "highlight_text")
- GUIFormSpecMenu::parseColor(value, m_highlight_text, false);
+ parseColorString(value, m_highlight_text, false);
else if (name == "opendepth")
opendepth = stoi(value);
else
// Append empty strings to content if there is an incomplete row
s32 cellcount = rowcount * colcount;
while (content.size() < (u32) cellcount)
- content.push_back("");
+ content.emplace_back("");
// Create temporary rows (for processing columns)
struct TempRow {
width = myround(em * 1.5); // default indent width
}
- for (size_t k = 0; k < columns[j].options.size(); ++k) {
- const std::string &name = columns[j].options[k].name;
- const std::string &value = columns[j].options[k].value;
+ for (const Option &option : columns[j].options) {
+ const std::string &name = option.name;
+ const std::string &value = option.value;
if (name == "padding")
padding = myround(stof(value) * em);
else if (name == "tooltip")
else if (columntype == COLUMN_TYPE_COLOR) {
for (s32 i = 0; i < rowcount; ++i) {
video::SColor cellcolor(255, 255, 255, 255);
- if (GUIFormSpecMenu::parseColor(content[i * colcount + j], cellcolor, true))
- rows[i].colors.push_back(std::make_pair(cellcolor, j+span));
+ if (parseColorString(content[i * colcount + j], cellcolor, true))
+ rows[i].colors.emplace_back(cellcolor, j+span);
}
}
else if (columntype == COLUMN_TYPE_INDENT ||
void GUITable::clear()
{
// Clean up cells and rows
- for (size_t i = 0; i < m_rows.size(); ++i)
- delete[] m_rows[i].cells;
+ for (GUITable::Row &row : m_rows)
+ delete[] row.cells;
m_rows.clear();
m_visible_rows.clear();
void GUITable::setSelected(s32 index)
{
+ s32 old_selected = m_selected;
+
m_selected = -1;
m_sel_column = 0;
m_sel_doubleclick = false;
- --index;
+ --index; // Switch from 1-based indexing to 0-based indexing
s32 rowcount = m_rows.size();
+ if (rowcount == 0 || index < 0) {
+ return;
+ }
- if (index >= rowcount)
+ if (index >= rowcount) {
index = rowcount - 1;
- while (index >= 0 && m_rows[index].visible_index < 0)
- --index;
+ }
+
+ // If the selected row is not visible, open its ancestors to make it visible
+ bool selection_invisible = m_rows[index].visible_index < 0;
+ if (selection_invisible) {
+ std::set<s32> opened_trees;
+ getOpenedTrees(opened_trees);
+ s32 indent = m_rows[index].indent;
+ for (s32 j = index - 1; j >= 0; --j) {
+ if (m_rows[j].indent < indent) {
+ opened_trees.insert(j);
+ indent = m_rows[j].indent;
+ }
+ }
+ setOpenedTrees(opened_trees);
+ }
+
if (index >= 0) {
m_selected = m_rows[index].visible_index;
assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
}
- autoScroll();
+ if (m_selected != old_selected || selection_invisible) {
+ autoScroll();
+ }
}
GUITable::DynamicData GUITable::getDynamicData() const
m_keynav_time = dyndata.keynav_time;
m_keynav_buffer = dyndata.keynav_buffer;
- m_scrollbar->setPos(dyndata.scrollpos);
-
setSelected(dyndata.selected);
m_sel_column = 0;
m_sel_doubleclick = false;
+
+ m_scrollbar->setPos(dyndata.scrollpos);
}
const c8* GUITable::getTypeName() const
client_clip.UpperLeftCorner.Y += 1;
client_clip.UpperLeftCorner.X += 1;
client_clip.LowerRightCorner.Y -= 1;
- client_clip.LowerRightCorner.X -=
- m_scrollbar->isVisible() ?
- skin->getSize(gui::EGDS_SCROLLBAR_SIZE) :
- 1;
+ client_clip.LowerRightCorner.X -= 1;
+ if (m_scrollbar->isVisible()) {
+ client_clip.LowerRightCorner.X =
+ m_scrollbar->getAbsolutePosition().UpperLeftCorner.X;
+ }
client_clip.clipAgainst(AbsoluteClippingRect);
// draw visible rows
return true;
}
- else if (event.KeyInput.PressedDown && (
+
+ if (event.KeyInput.PressedDown && (
event.KeyInput.Key == KEY_LEFT ||
event.KeyInput.Key == KEY_RIGHT)) {
// Open/close subtree via keyboard
}
else if (event.KeyInput.PressedDown && event.KeyInput.Char) {
// change selection based on text as it is typed
- s32 now = getTimeMs();
+ u64 now = porting::getTimeMs();
if (now - m_keynav_time >= 500)
m_keynav_buffer = L"";
m_keynav_time = now;
// Update tooltip
setToolTipText(cell ? m_strings[cell->tooltip_index].c_str() : L"");
+ // Fix for #1567/#1806:
+ // IGUIScrollBar passes double click events to its parent,
+ // which we don't want. Detect this case and discard the event
+ if (event.MouseInput.Event != EMIE_MOUSE_MOVED &&
+ m_scrollbar->isVisible() &&
+ m_scrollbar->isPointInside(p))
+ return true;
+
if (event.MouseInput.isLeftPressed() &&
(isPointInside(p) ||
event.MouseInput.Event == EMIE_MOUSE_MOVED)) {
sel_doubleclick) {
sendTableEvent(sel_column, sel_doubleclick);
}
+
+ // Treeview: double click opens/closes trees
+ if (m_has_tree_column && sel_doubleclick) {
+ toggleVisibleTree(m_selected, 0, false);
+ }
}
}
return true;
std::map<std::string, s32>::iterator it = m_alloc_strings.find(text);
if (it == m_alloc_strings.end()) {
s32 id = m_strings.size();
- std::wstring wtext = narrow_to_wide(text);
- m_strings.push_back(core::stringw(wtext.c_str()));
+ std::wstring wtext = utf8_to_wide(text);
+ m_strings.emplace_back(wtext.c_str());
m_alloc_strings.insert(std::make_pair(text, id));
return id;
}
- else {
- return it->second;
- }
+
+ return it->second;
}
s32 GUITable::allocImage(const std::string &imagename)
m_alloc_images.insert(std::make_pair(imagename, id));
return id;
}
- else {
- return it->second;
- }
+
+ return it->second;
}
void GUITable::allocationComplete()
{
if (i >= 0 && i < (s32) m_visible_rows.size())
return &m_rows[m_visible_rows[i]];
- else
- return NULL;
+
+ return NULL;
}
bool GUITable::doesRowStartWith(const Row *row, const core::stringw &str) const
really_hovering = true;
return i;
}
- else if (i < 0)
+ if (i < 0)
return 0;
- else
- return rowcount - 1;
+ return rowcount - 1;
}
s32 GUITable::getCellAt(s32 x, s32 row_i) const
if (rel_x >= cell->xmin && rel_x <= cell->xmax)
return pivot;
- else if (rel_x < cell->xmin)
+
+ if (rel_x < cell->xmin)
jmax = pivot - 1;
else
jmin = pivot + 1;
rel_x >= row->cells[jmin].xmin &&
rel_x <= row->cells[jmin].xmax)
return jmin;
- else
- return -1;
+
+ return -1;
}
void GUITable::autoScroll()
void GUITable::setOpenedTrees(const std::set<s32> &opened_trees)
{
- s32 old_selected = getSelected();
+ s32 old_selected = -1;
+ if (m_selected >= 0)
+ old_selected = m_visible_rows[m_selected];
std::vector<s32> parents;
std::vector<s32> closed_parents;
updateScrollBar();
- setSelected(old_selected);
+ // m_selected must be updated since it is a visible row index
+ if (old_selected >= 0)
+ m_selected = m_rows[old_selected].visible_index;
}
void GUITable::openTree(s32 to_open)