Jump to content

Ultra App Kit - table


mekans
 Share

Go to solution Solved by Dreikblack,

Recommended Posts

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
Link to comment
Share on other sites

  • 5 months later...

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

Link to comment
Share on other sites

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
Link to comment
Share on other sites

  • Solution

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
Link to comment
Share on other sites

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.

 Share

×
×
  • Create New...