listview and add item

Started by billarhos, 24 February 2019, 16:03:52

billarhos

Hi Texus

You have:

1 : std::size_t addItem(const sf::String& text);
2: std::size_t addItem(const std::vector<sf::String>& item);
3 : bool removeItem(std::size_t index);

4 : void setSelectedItem(std::size_t index);
5: int getSelectedItemIndex() const;
i am sure that i forget some other functions


1. Add item as a row or in a specific cell in a specific column

std::size_t addItem(std::size_t columnIndex, const sf::String& text);


2: should be

std::size_t addRow(const std::vector<sf::String>& item);


3. is remove item - or remove row
4. setSelectedItem on which column
5. is selected row or selected a specific cell in a specific column

there is a need to be distiguish the cell index and the column index.
Also there is need for a "setCell(column, cell, text)" function like :

void setColumnText(std::size_t index, const sf::String& text);


and may be a


std::size_t addMultiRows(const std::vector<std::vector<sf::String>> & item);


thank you



texus

Although I don't want the API to be the same as the corresponding Windows component, I was trying to keep it somewhat similar. So I'm a bit hesitant to make some of these changes.

1. I see an item as an entire row, so an addItem function that takes a column argument doesn't fit with my vision of the ListView. I consider the "addItem(const sf::String& text)" to only exist for convenience in case someone just uses the widget for a single column or in case I add an addSubItem function in the future. The "addItem(const std::vector<sf::String>& item)" is what I consider the "real" addItem function.

2. I could rename it to addRow or addItemRow though, but the reason would be to have the naming more consistent with getItemRow (which only uses the term "row" because I couldn't find a better word and the "getItem" function already existed).

3. Similarly, removeItem removes an entire row because the row is the item.

4-5. I'm not sure how to best deal with selecting a single cell, the ListView doesn't need to support something like that imho. If you have some use case for it then maybe I can still add an option to select a cell instead of a row though, but it wouldn't be the default behavior.

A way to update an item is still lacking, so I can definately add something like "bool updateItem(std::size_t index, std::vector<sf::String>)". Maybe I'll add something like "updateSubItem(std::size_t rowIndex, std::size_t columnIndex, sf::String)" too, but the updateItem is the most important one as there is currently no way at all to change an item.

A "void addMultipleItems(const std::vector<std::vector<sf::String>> & item)" could be added, but only for performance reasons. In terms of usability, you can just call addItem in a loop, so there isn't much need for such a function. But if you are working with over 100 items then it could still be useful because it would insert the items slightly faster. Although I wonder if the difference would be big enough to notice, that is something that I should test first.

billarhos

Ok Texus. I respect that you want to keep the different namethology than windows.

So, "updateItem" and "updateSubItem" are good with me. And there are plenty of use cases that need both of these functions.

Also i know many cases that you could add more than 100 items. Sometimes, there is a need to load more than 1000 items. Look at "Mp3tag", Virtual Dj" or similar programs. Recently i used "Mp3tag" where i had to load some music folders with more than 10000 mp3 files altogether. And that's the reason for a "addMultipleItems" function.

I didn't check if you have a right click on a item-row! Have you?




texus

#3
There isn't a right click event yet, but I could add a "ItemRightClicked" signal.

I did some performance tests and for 5000 items the addMultipleItems can reduce the time from 0.168 to 0.045 seconds.
I'll add it, but most people won't need it. Inserting 1000 items with addItem takes only 0.04 seconds, which will hardly be noticeable when doing it only once during the loading phase.
Edit: That was in debug mode, inserting 5000 items with addItem only takes 0.06 seconds in release mode.

billarhos

That is quite fast. How many columns your test have? Have you made any test with 5 or more columns?

texus

#5
The test I did was just to get a quick idea of the performance, it wasn't very realistic: all items were identical and there was only one column.
With 10000 items and 10 columns (still identical items of only 6 characters) it takes about 0.32 seconds in release mode. Using addMultipleItems only decreases it to 0.2 - 0.26 seconds (depending on whether you include constructing the vector in the measurements). So the difference itsn't that spectacular (difference is smaller in release than it was in debug) as the biggest performance cost is actually creating the items. The performance scales pretty linearly, 10000 items is about 10 times slower than 1000 items. For columns the same goes: the performance of 10000 items in a single column should be comparable to 1000 items with 10 columns.

Edit: I might even be able to reduce the time by another 10-15% if I don't unnecessary create a sprite for the icon, but I think the current performance is already great.

billarhos

I know that listview is a demanding widget. A lot can be done in future to improve speed and functionality. For example, it would be great to add a "search-text-box" in new widgets in trello. Even windows does not have this widget. Searching a "listview" for a specific text can be done by binding a "listview" widget with a "searchbar" widget and easily exclude rows to show results. Also this functionality can be added to current "textbox" and also expanded for labels or text boxes where people need to look for a specific string. (aka ctrl+f)

texus

Could you give an example of how such a search box would look visually? Because I'm not sure what you mean and how you expect it to be used. And how do you expect searched text to show up in Label or TextBox?

billarhos


QuoteAlso this functionality can be added to current "textbox"

So a search box is actually a text box. Just see on top right on visual studio or press "ctrl+F" in notepad. The text found (if found) is highlighted! Easy!

However in a listview the lines-rowes that does not contain the request searched string must be excluded.

texus

Highlighting is something that won't happen soon, a RichTextBox class would first have to exist. The TextBox text is currently split in up to 5 parts to draw it, highlighting would require it to be split in an unlimited amount of parts (as it depends on how many matches there were).
An editor with highlighting and searching though it is not something that is likely to ever exist in TGUI. You would have to write it yourself with a couple of buttons (e.g. goto previous and next match), an edit box (to type the searched word) and a RichTextLabel or RichTextBox that allows setting colors of any part of the text. The RichText widget is still missing in TGUI and may be included in the future, although there are currently no plans for it as it would be a lot of work.

The ListView case should probably just be implemented in your own code, that gives a lot more flexibility (e.g. to search only at the beginning of the word or only search in first column, etc).

billarhos

Maybe a highlighted text is identical with selected text. And there is selected text in text box.

Normally the search in a listview apply to all columns but also this is not a huge handicap .

And yes everything that i thought about can be implemented in our code. ;D

billarhos

A "setSelectedText(size_t indexBegin, size_t sizeLetters)" function for textbox widget would be useful in many cases.

Also can not found setHorizontalAlignment and setVerticalAlignment for textbox. Have you miss them for any particular reason?



texus

QuoteMaybe a highlighted text is identical with selected text. And there is selected text in text box.
Still easier said than done, the TextBox hardcodes that there is only one part selected. Implementing something like highlighting in TextBox might not be that hard but currently has very low priority.

QuoteA "setSelectedText(size_t indexBegin, size_t sizeLetters)" function for textbox widget would be useful in many cases.
I can't immediately think of a use case for it, the TextBox is meant to write text in, not for visualizing data.

QuoteAlso can not found setHorizontalAlignment and setVerticalAlignment for textbox. Have you miss them for any particular reason?
Alignment in a TextBox is an extremely niche use case imho, I don't think I've ever encountered it in any application. I can understand that it could be useful so I'll add it to the todo list, but it will be harder to add than in Label where there is no text interaction.

billarhos

QuoteMaybe a highlighted text is identical with selected text. And there is selected text in text box.

My mistake. In this phrase i should added one more sentence. This phrase:

QuoteSo you can use selected text instead of highlight text.

So, forget everything about "hightlighted" text.

A function like setSelectedText(size_t indexBegin, size_t sizeLetters) can be used in any case of making (in our code) a search functionality. After all in textbox there is a "getSelectedText" function.

I realize that "setHorizontalAlignment and setVerticalAlignment for textbox" were missing when yesterday i tried to make an example while we were talking about searching functionality in textboxes. I overcome this lack by declearing bigger font size. But at the end i never finished this example due to lack of "setSelectedText" function.




texus

#14
The following things have been added:
- RightClicked signal in ListView
- addMultipleItems, changeItem and changeSubItem functions in ListView
- setSelectedText function in TextBox

Edit: The unit tests are hanging somewhere on the right click event on windows (while the tests don't hang there on linux), so right click might not work yet. I'll try to figure that out tomorrow.

billarhos

Check this out



#if defined NDEBUG
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#endif

#define USE_TGUI
#include "SFML_libs.hpp"

#include <TGUI/TGUI.hpp>

using namespace std;

bool running = true;

//https://scitechdaily.com/chandra-solves-the-universes-missing-mass-problem/
const std::string dataStr =
{
"New results from NASA’s Chandra X - ray Observatory may have helped solve the Universe’s “missing mass” problem. Astronomers cannot account for about a third of the normal matter â€" that is, hydrogen, helium, and other elements â€" that were created in the first billion years or so after the Big Bang."
"Scientists have proposed that the missing mass could be hidden in gigantic strands or filaments of warm(temperature less than 100,000 Kelvin) and hot(temperature greater than 100,000 K) gas in intergalactic space.These filaments are known by astronomers as the “warm - hot intergalactic medium” or WHIM."
"They are invisible to optical light telescopes, but some of the warm gas in filaments has been detected in ultraviolet light.The main part of this graphic is from the Millennium simulation, which uses supercomputers to formulate how the key components of the Universe, including the WHIM, would have evolved over cosmic time."
"If these filaments exist, they could absorb certain types of light such as X - rays that pass through them.The inset in this graphic represents some of the X - ray data collected by Chandra from a distant, rapidly - growing supermassive black hole known as a quasar.The plot is a spectrum â€" the amount of X - rays over a range"
"of wavelengths â€" from a new study of the quasar H1821 + 643 that is located about 3.4 billion light years from Earth."
"The latest result uses a new technique that both hones the search for the WHIM carefully and boosts the relatively weak absorption signature by combining different parts of the spectrum to find a valid signal.With this technique, researchers identified 17 possible filaments lying between the quasar and Earth, and obtained their distances."
};

bool matchCase(false);
bool matchWholeWord(false);

std::string::size_type  search_Index = std::string::npos;

std::string lower_string(const std::string& str)
{
string lower;
if (matchCase)
lower = str;
else
transform(str.begin(), str.end(), std::back_inserter(lower), tolower);
return lower;
}

std::string::size_type find_str_ci(const std::string& str, const std::string& substr)
{
if (matchWholeWord)
{
string findStr = " " + substr + " ";
return lower_string(str).find(lower_string(findStr));
}
return lower_string(str).find(lower_string(substr));
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
window.setFramerateLimit(60);

tgui::Gui gui(window);

auto pTextBox = tgui::TextBox::create();
pTextBox->setPosition(50, 100);
pTextBox->setSize(700, 470);
pTextBox->setTextSize(16);
pTextBox->setText(dataStr);
gui.add(pTextBox);

auto pSearchTextBox = tgui::TextBox::create();
pSearchTextBox->setPosition(50, 50);
pSearchTextBox->setSize(200, 30);
pSearchTextBox->setTextSize(16);

pSearchTextBox->connect("TextChanged", [&]()
{
search_Index = find_str_ci(dataStr.c_str(), pSearchTextBox->getText().toAnsiString().c_str());

if (search_Index != std::string::npos)
{
pTextBox->setSelectedText(matchWholeWord ? search_Index + 1: search_Index, matchWholeWord ? search_Index + pSearchTextBox->getText().toAnsiString().size() + 1 : search_Index + pSearchTextBox->getText().toAnsiString().size());
}
else
{
pTextBox->setSelectedText(0, 0);
}
});
gui.add(pSearchTextBox);

auto pCheckBoxCase = tgui::CheckBox::create();
pCheckBoxCase->setPosition(300, 50);
pCheckBoxCase->setText("Match case");
pCheckBoxCase->setSize(25, 25);
pCheckBoxCase->connect("Changed", [&]()
{
matchCase = !matchCase;
pTextBox->setSelectedText(0, 0);
pSearchTextBox->setText("");
});
gui.add(pCheckBoxCase);

auto pCheckBoxWord = tgui::CheckBox::create();
pCheckBoxWord->setPosition(470, 50);
pCheckBoxWord->setText("Match whole word");
pCheckBoxWord->setSize(25, 25);
pCheckBoxWord->connect("Changed", [&]()
{
matchWholeWord = !matchWholeWord;
pTextBox->setSelectedText(0, 0);
pSearchTextBox->setText("");
});

gui.add(pCheckBoxWord);

while (running)
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
{
running = false;
}
break;
case sf::Event::MouseButtonPressed:
{

}
break;
case sf::Event::KeyPressed:
{
if (event.key.code == sf::Keyboard::Escape)
running = false;
break;
}
}

gui.handleEvent(event);
}

window.clear(sf::Color(200, 200, 200, 255));
gui.draw();
window.display();
}

return EXIT_SUCCESS;
}

texus

Looks good. Seems like setSelectedText is indeed enough to implement searching and basic highlighting in your own code.

I fixed the issue with right click (which was actually just undefined behavior when removing an item while the mouse hovered over the last item in the list).