Jump to content

Ultra App Kit - table


Go to solution Solved by Dreikblack,

Recommended Posts

Posted

Hi there,
How to create table in ultra app kit? I mean table with headers, columns and also selection highlight and hover highlight? From "/learn" i think that i need to create it from CustomWidget, but for newbie like me it's hard. Anyone help?

Posted

Cell.h:

#pragma once
#include "UltraEngine.h"
#include "Table.h"

using namespace UltraEngine;

class Cell : public TextField
{
protected:
	Cell();
public:
	static std::shared_ptr<Cell> create(const int x, const int y, const int width, const int height, shared_ptr<Table> table, bool isHeader = false);
	void MouseEnter(const int x, const int y);
	void MouseLeave(const int x, const int y);
	Vec4 highlightColor = Vec4(0.3f, 0.3f, 0.3f, 1.0f);
	Vec4 backgroundColor = Vec4(0.15f, 0.15f, 0.15f, 1.0f);
};

Cell.cpp:

#include "UltraEngine.h"
#include "Cell.h"

Cell::Cell()
{
}

std::shared_ptr<Cell> Cell::create(const int x, const int y, const int width, const int height, shared_ptr<Table> table, bool isHeader)
{
	struct Struct : public Cell {
	};
	auto instance = std::make_shared<Struct>();
	instance->As<Widget>()->Initialize("", x, y, width, height, table, TEXTFIELD_DEFAULT);
	if (isHeader) {
		instance->backgroundColor = Vec4(0.2f, 0.2f, 0.2f, 1.0f);
		instance->highlightColor = Vec4(0.35f, 0.35f, 0.35f, 1.0f);
		instance->SetColor(instance->backgroundColor, WIDGETCOLOR_SUNKEN);
	}
	return instance;
}

void Cell::MouseEnter(const int x, const int y)
{
	TextField::MouseEnter(x, y);
	SetColor(highlightColor, WIDGETCOLOR_SUNKEN);
}

void Cell::MouseLeave(const int x, const int y)
{
	TextField::MouseEnter(x, y);
	SetColor(backgroundColor, WIDGETCOLOR_SUNKEN);
}

Table.h

#pragma once
#include "UltraEngine.h"

class Cell;

using namespace UltraEngine;

class Table : public Panel
{
protected:
	Table();

public:
	int rowCount = 0;
	int columnCount = 0;
	vector<std::shared_ptr<Cell>> headers;
	static std::shared_ptr<Table> create(const int x, const int y, const int width, const int height, int columnCount, int rowCount, shared_ptr<Widget> parent);
	vector<vector<std::shared_ptr<Cell>>> cells;
};

Table.cpp

#include "UltraEngine.h"
#include "Table.h"
#include "Cell.h"

using namespace UltraEngine;

Table::Table()
{
}

std::shared_ptr<Table> Table::create(const int x, const int y, const int width, const int height, int columnCount, int rowCount, shared_ptr<Widget> parent)
{
	struct Struct : public Table {
	};
	auto instance = std::make_shared<Struct>();
	instance->Initialize("", x, y, width, height, parent, UltraEngine::PanelStyle::PANEL_DEFAULT);
	instance->SetColor(0, 0, 0, 0);
	instance->columnCount = columnCount;
	instance->rowCount = rowCount;
	int cellWidth = Floor (float(width) / float(columnCount));
	int cellHeight = Floor(float(height) / float(rowCount + 1));

	instance->cells.resize(columnCount);
	instance->headers.resize(columnCount);

	for (int i = 0; i < columnCount; i++) {
		instance->cells[i].resize(rowCount);
		instance->headers[i] = Cell::create(cellWidth * i, 0, cellWidth, cellHeight, instance, true);
		instance->headers[i]->SetText(WString("Header ") + WString(i));
	}
	for (int i = 0; i < columnCount; i++) {
		for (int j = 0; j < rowCount; j++) {
			instance->cells[i][j] = (Cell::create(cellWidth * i, cellHeight * (j+1) , cellWidth, cellHeight, instance));
			instance->cells[i][j]->SetText(WString("Cell ") + WString(i) + WString(',') + WString(j));
		}
	}
	return instance;
}

main.cpp

#include "UltraEngine.h"
#include "UI/Table.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    auto displays = GetDisplays();
    auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, displays[0], WINDOW_TITLEBAR | WINDOW_RESIZABLE | WINDOW_CENTER);
    auto ui = CreateInterface(window);

    Table::create(0, 0, 500, 500, 3, 7, ui->root);

    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        const Event ev = WaitEvent();
        switch (ev.id)
        {     
            case EVENT_QUIT:
            case EVENT_WINDOWCLOSE:
                return 0;
                break;
            default: break;
        }
    }
    return 0;
}

 

Table.png

  • Like 2
  • Thanks 1
  • 5 months later...
Posted

i adjusted this solution in some way, but i need now table like listview in c#. It should be columns, headers and rows that could be selected. in a picture we can see that Cell0,0 1.0 and 2.0 are separate cells with data - i need them as a full row that are related to one object and displaying this object data.

Tbh i tried to build a custom widget with rows and other stuff but it's too hard for me

Posted

Done!

image.thumb.png.100cc8e37ac106ada5995b3aa07e0cb4.png

ListViewData.cpp:

#pragma once
#include <UltraEngine.h>

using namespace UltraEngine;

class ListViewData 
{
protected:
	ListViewData() {}
public:
	static std::shared_ptr<ListViewData> create()
	{
		struct Struct : public ListViewData {};
		auto instance = std::make_shared<Struct>();
		return instance;
	}

	static std::shared_ptr<ListViewData> create(vector<WString> fields)
	{
		struct Struct : public ListViewData {};
		auto instance = std::make_shared<Struct>();
		instance->fields = fields;
		return instance;
	}

	vector<WString> fields;

	bool operator==(const ListViewData& other) const
	{
		if (fields.size() != other.fields.size()) return false;
		for (int i = 0; i < fields.size(); i++)
		{
			if (fields[i] != other.fields[i])
			{
				return false;
			}
		}
		return true;
	}
};

ListView.h

#pragma once
#include <UltraEngine.h>
#include "ListViewData.cpp"

using namespace UltraEngine;

class ListView : public Widget
{
protected:
	ListView();
	virtual bool Initialize(const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, shared_ptr<ListViewData> header, int columnCount);
	iVec2 itemSize = iVec2(100, 20);
	//Called each time the widget is redrawn
	virtual void Draw(const int x, const int y, const int width, const int height);
	//Called when the mouse button is pressed
	virtual void MouseDown(const MouseButton button, const int x, const int y);
	//Called when the mouse moves if this widget has the focus
	virtual void MouseMove(const int x, const int y);
	std::function<bool(Event)> pickItemListener;
	int initBlockCount = 4;
	int selectedItemId = -1;
	int highlightItemId = -1;
	int columnCount = 1;
	int textAlignment = TEXT_CENTER | TEXT_MIDDLE;
	vector <shared_ptr<ListViewData>> items;
	shared_ptr<ListViewData> header;
public:
	static std::shared_ptr<ListView> create(const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, shared_ptr<ListViewData> header, int columnCount = 1);
	virtual void Show();
	int getItemHeight();
	void setListener(std::function<bool(Event)> listener);
	void addItem(shared_ptr<ListViewData> item);
	void addItems(vector<shared_ptr<ListViewData>> items);
	void selectItem(shared_ptr<ListViewData> item);
	void removeSelectedItem();
	void clearItems();
	vector <shared_ptr<ListViewData>> getItems();
	shared_ptr<ListViewData> getSelectedItem();
	int getItemCount();
	int getHeight();
	void resize();
};

ListView.cpp:

#include "UltraEngine.h"
#include "ListView.h"

ListView::ListView()
{
	blocks.resize(initBlockCount);//background, border, highlight background for selected item, highlight under cursor
	textAlignment = TEXT_MIDDLE;
}

std::shared_ptr<ListView> ListView::create(const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, shared_ptr<ListViewData> header, int columnCount)
{
	struct Struct : public ListView {};
	auto instance = std::make_shared<Struct>();
	instance->Initialize(x, y, width, height, parent, header, columnCount);
	return instance;
}

bool ListView::Initialize(const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, shared_ptr<ListViewData> header, int columnCount)
{
	bool isInit = Widget::Initialize("", x, y, width, height, parent, 0);
	itemSize = iVec2(width, getItemHeight());
	ListView::columnCount = columnCount;
	initBlockCount += (columnCount * 2);
	ListView::header = header;
	return isInit;
}

int ListView::getItemHeight()
{
	return Round(float(GetInterface()->GetFontHeight(font, fontscale, fontweight)));
}

void ListView::Draw(const int x, const int y, const int width, const int height)
{
	for (auto& block : blocks)
	{
		block.hidden = true;
	}
	//Background
	blocks[0].color = color[WIDGETCOLOR_SUNKEN];
	blocks[0].wireframe = false;
	blocks[0].position = iVec2(0);
	blocks[0].size = size;
	blocks[0].hidden = false;
	//Border
	blocks[1].hidden = false;
	blocks[1].color = color[WIDGETCOLOR_BORDER];
	blocks[1].wireframe = true;
	blocks[1].position = iVec2(0);
	blocks[1].size = size;
	blocks[1].radius = 0;
	
	//Highlight for selected
	if (selectedItemId >= 0) {
		blocks[2].color = color[WIDGETCOLOR_SUNKEN] * 0.6f;
		blocks[2].wireframe = false;
		blocks[2].position = iVec2(0, itemSize.height * (selectedItemId + 1));
		blocks[2].size = itemSize;
		blocks[2].hidden = false;
	}
	//Highlight under cursor
	if (highlightItemId >= 0 && highlightItemId != selectedItemId) {
		blocks[3].color = color[WIDGETCOLOR_SUNKEN] * 0.85f;
		blocks[3].wireframe = false;
		blocks[3].position = iVec2(0, itemSize.height * (highlightItemId + 1));
		blocks[3].size = itemSize;
		blocks[3].hidden = false;
	}

	int fieldWidth = itemSize.width / columnCount;
	//headers
	for (int column = 0; column < columnCount; column++)
	{
		blocks[4 + column].hidden = false;
		blocks[4 + column].position = iVec2(fieldWidth * column, 0);
		blocks[4 + column].size = iVec2(fieldWidth, itemSize.height);
		blocks[4 + column].SetText(header->fields[column]);
		blocks[4 + column].textalignment = textAlignment;
		blocks[4 + column].color = 1;

		blocks[4 + column + columnCount].hidden = false;
		blocks[4 + column + columnCount].position = iVec2(fieldWidth * column, 0);
		blocks[4 + column + columnCount].size = iVec2(fieldWidth, itemSize.height);
		blocks[4 + column + columnCount].wireframe = true;
		blocks[4 + column + columnCount].color = Vec4(0.6f, 0.6f, 0.6f, 1);
	}
	// items
	int blockSize = initBlockCount + items.size();
	for (int column = 0; column < columnCount; column++)
	{
		int extraFiledI = items.size() * column;
		auto iterItem = items.begin();
		for (int i = initBlockCount; i < blockSize; i++)
		{
			blocks[i + extraFiledI].hidden = false;
			blocks[i + extraFiledI].position = iVec2(fieldWidth * column, itemSize.height * (i - initBlockCount + 1));
			blocks[i + extraFiledI].size = iVec2(fieldWidth, itemSize.height);
			blocks[i + extraFiledI].SetText(iterItem->get()->fields[column]);
			blocks[i + extraFiledI].textalignment = textAlignment;
			blocks[i + extraFiledI].color = 1;
			iterItem++;
		}
		for (int i = initBlockCount; i < blockSize; i++)
		{
			blocks[items.size() * columnCount + i + extraFiledI].hidden = false;
			blocks[items.size() * columnCount + i + extraFiledI].position = iVec2(fieldWidth * column, itemSize.height * (i - initBlockCount + 1));
			blocks[items.size() * columnCount + i + extraFiledI].size = iVec2(fieldWidth, itemSize.height);
			blocks[items.size() * columnCount + i + extraFiledI].wireframe = true;
			blocks[items.size() * columnCount + i + extraFiledI].color = color[WIDGETCOLOR_BORDER];
		}
	}
}

void ListView::MouseDown(const MouseButton button, const int x, const int y)
{
	if (button == MOUSE_LEFT)
	{
		if (x >= 0 and y >= getItemHeight() and x < size.x and y < size.y)
		{
			int itemId = y / getItemHeight() - 1;
			if (itemId >= 0 and itemId < items.size())
			{
				selectedItemId = itemId;
				Redraw();
				if (pickItemListener)
				{
					pickItemListener(Event(EVENT_WIDGETACTION, Self(), selectedItemId));
				}
			}
		}
	}
}

void ListView::MouseMove(const int x, const int y)
{
	int oldValue = highlightItemId;
	if (x >= 0 and y >= getItemHeight() and x < size.x and y < size.y)
	{
		int itemId = y / getItemHeight() - 1;
		if (itemId >= 0 and itemId < items.size())
		{
			highlightItemId = itemId;
		}
		else {
			highlightItemId = -1;
		}
	}
	else
	{
		highlightItemId = -1;
	}
	if (oldValue != highlightItemId)
	{
		Redraw();
	}
}

void ListView::Show()
{
	highlightItemId = -1;
}

void ListView::addItem(shared_ptr<ListViewData> item)
{
	items.push_back(item);
	resize();
	Redraw();
}

void ListView::addItems(vector<shared_ptr<ListViewData>> newItems)
{
	items.insert(items.end(), newItems.begin(), newItems.end());
	resize();
	Redraw();
}

void ListView::selectItem(shared_ptr<ListViewData> item)
{
	int i = 0;
	for (shared_ptr<ListViewData>& currentItem : items) {
		if (currentItem == item)
		{
			selectedItemId = i;
			break;
		}
		i++;
	}
}

void ListView::removeSelectedItem()
{
	if (selectedItemId == -1) return;
	items.erase(items.begin() + selectedItemId);
	//for some reason without readding items with just erase + Redraw() some fields has visual glitches
	auto newItems = items;
	clearItems();
	addItems(newItems);
}

void ListView::clearItems()
{
	selectedItemId = -1;
	highlightItemId = -1;
	items.clear();
	blocks.resize(initBlockCount);
	Redraw();
}

vector<shared_ptr<ListViewData>> ListView::getItems()
{
	return items;
}

shared_ptr<ListViewData> ListView::getSelectedItem()
{
	if (selectedItemId >= 0 && selectedItemId < getItemCount())
	{
		return items[selectedItemId];
	}
	return nullptr;
}

int ListView::getItemCount()
{
	return items.size();
}

int ListView::getHeight()
{
	return GetSize().height;
}

void ListView::resize()
{
	blocks.resize(initBlockCount + items.size() * 2 * columnCount);
}

void ListView::setListener(std::function<bool(Event)> listener)
{
	pickItemListener = listener;
}

main.cpp:

#include "UltraEngine.h"
#include "ListView.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
	//Get the displays
	auto displays = GetDisplays();

	//Create a window
	auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, displays[0], WINDOW_TITLEBAR | WINDOW_RESIZABLE | WINDOW_CENTER);

	//Create User Interface
	auto ui = CreateInterface(window);

	//Create widget
	auto sz = ui->root->GetSize();

	auto listView = ListView::create(10, 10, 600, 300, ui->root, ListViewData::create({ WString("header0 "), WString("header1") }), 2);
	for (int i = 0; i < 2; i++)
	{
		listView->addItem(ListViewData::create({ WString("filed0 ") + WString(i), WString("filed1 ") + WString(i) }));
	}


	auto textField0 = CreateTextField(10, 320, 100, 20, ui->root);
	textField0->SetText("filed0");
	auto textField1 = CreateTextField(110, 320, 100, 20, ui->root);
	textField1->SetText("filed1");
	auto addBtn = CreateButton("Add", 10, 340, 100, 20, ui->root);
	auto removeBtn = CreateButton("Remove", 110, 340, 100, 20, ui->root);

	listView->setListener(
		[textField0, textField1, listView](Event event)
		{
			auto data = listView->getSelectedItem();
			textField0->SetText(data->fields[0]);
			textField0->Redraw();
			textField1->SetText(data->fields[1]);
			textField1->Redraw();

			return true;
		});
	while (true)
	{
		const Event ev = WaitEvent();
		switch (ev.id)
		{
		case EVENT_WIDGETACTION:
			if (ev.source == addBtn)
			{
				listView->addItem(ListViewData::create({ WString(textField0->GetText()), WString(textField1->GetText()) }));
			}
			if (ev.source == removeBtn)
			{
				listView->removeSelectedItem();
			}
			break;
		case EVENT_QUIT:
		case EVENT_WINDOWCLOSE:
			return 0;
			break;
		default: break;
		}
	}
	return 0;
}

 

  • Like 2
Posted

In Ultra Engine, there is a new command that has not been documented yet called PopupMenu().

My job is to make tools you love, with the features you want, and performance you can't live without.

  • Solution
Posted

Edit button just for having 2 buttons in a menu:

image.thumb.png.82120b2d0867bbc155716c15e1fb1788.png

add  shared_ptr<Panel> contextMenu; to ListView.h

In ListView.cpp update Initialize method:

bool ListView::Initialize(const int x, const int y, const int width, const int height, shared_ptr<Widget> parent, shared_ptr<ListViewData> header, int columnCount)
{
	bool isInit = Widget::Initialize("", x, y, width, height, parent, 0);
	itemSize = iVec2(width, getItemHeight());
	ListView::columnCount = columnCount;
	initBlockCount += (columnCount * 2);
	ListView::header = header;
	contextMenu = CreatePanel(0, 0, 100, 40, gui->root);
	contextMenu->Hide();
	auto editButton = CreateButton("Edit", 0, 0, 100, 20, contextMenu);//just for an example, no function
	auto removeButton = CreateButton("Remove", 0, 20, 100, 20, contextMenu);
	ListenEvent(EVENT_WIDGETACTION, removeButton, RemoveCallback, Self()->As<ListView>());
	return isInit;
}

and MouseDown method:

void ListView::MouseDown(const MouseButton button, const int x, const int y)
{
	contextMenu->Hide();
	if (x >= 0 and y >= getItemHeight() and x < size.x and y < size.y)
	{
		int itemId = y / getItemHeight() - 1;
		if (itemId >= 0 and itemId < items.size())
		{
			selectedItemId = itemId;
			Redraw();
			if (button == MOUSE_LEFT)
			{
				if (pickItemListener)
				{
					pickItemListener(Event(EVENT_WIDGETACTION, Self(), selectedItemId));
				}
			}
			else if (button == MOUSE_RIGHT)
			{
				contextMenu->Show();
				contextMenu->SetShape(iVec2(x, y), contextMenu->GetSize());
			}
		}
	}
}

Also add this function to same class:

bool RemoveCallback(const Event& ev, shared_ptr<Object> extra)
{
	auto listView = extra->As<ListView>();
	listView->removeSelectedItem();
	listView->contextMenu->Hide();
	return true;
}

 

  • Thanks 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...