mekans Posted December 28, 2022 Share Posted December 28, 2022 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? Quote Link to comment Share on other sites More sharing options...
Dreikblack Posted December 30, 2022 Share Posted December 30, 2022 Hi! I may try to create own Table class on the weekend. 1 Quote Link to comment Share on other sites More sharing options...
mekans Posted December 30, 2022 Author Share Posted December 30, 2022 Thank you, I am waiting! ❤️ Quote Link to comment Share on other sites More sharing options...
Dreikblack Posted December 30, 2022 Share Posted December 30, 2022 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; } 2 1 Quote Link to comment Share on other sites More sharing options...
mekans Posted December 31, 2022 Author Share Posted December 31, 2022 It works! Thank you, this source helped me to understand how it's created Quote Link to comment Share on other sites More sharing options...
mekans Posted June 17, 2023 Author Share Posted June 17, 2023 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 Quote Link to comment Share on other sites More sharing options...
Dreikblack Posted June 18, 2023 Share Posted June 18, 2023 Done! 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; } 2 Quote Link to comment Share on other sites More sharing options...
mekans Posted June 18, 2023 Author Share Posted June 18, 2023 Exacly what i wanted! I am missing only context menu and also scrollbar, could you help in therms of that topic too? Quote Link to comment Share on other sites More sharing options...
Josh Posted June 18, 2023 Share Posted June 18, 2023 The scrollpanel widget here might help; https://github.com/UltraEngine/Extras/tree/main/Code/C%2B%2B/CustomWidgets Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
mekans Posted June 18, 2023 Author Share Posted June 18, 2023 what about context menu? can this be done? Quote Link to comment Share on other sites More sharing options...
Josh Posted June 18, 2023 Share Posted June 18, 2023 In Ultra Engine, there is a new command that has not been documented yet called PopupMenu(). Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
mekans Posted June 18, 2023 Author Share Posted June 18, 2023 I bought UAK and to get this function i need to buy another framework? Just need to create gui for desktop apps not games Quote Link to comment Share on other sites More sharing options...
Dreikblack Posted June 18, 2023 Share Posted June 18, 2023 I may try to make something like that for UAK a bit later. 1 Quote Link to comment Share on other sites More sharing options...
Solution Dreikblack Posted June 18, 2023 Solution Share Posted June 18, 2023 Edit button just for having 2 buttons in a menu: 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; } 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.