// MIT License // // Copyright (C) 2018-2023, Tellusim Technologies Inc. https://tellusim.com/ // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include #include #include #include #include #include /* */ using namespace Tellusim; /* */ int32_t main(int32_t argc, char **argv) { DECLARE_WINDOW if(!window) return 1; // create window String title = String::format("%s Tellusim::Controls", window.getPlatformName()); if(!window.create(title) || !window.setHidden(false)) return 1; // create device Device device(window); if(!device) return 1; // create canvas Canvas canvas; // create root control ControlRoot root(canvas, true); // mouse callbacks window.setMouseRotatedCallback([&](Window::Axis axis, float32_t value) { root.setMouseAxis(translate_axis(axis), value); }); // keyboard callbacks window.setKeyboardPressedCallback([&](uint32_t key, uint32_t code) { root.setKeyboardKey(translate_key(key, true), code, true); if(key == Window::KeyEsc) window.stop(); }); window.setKeyboardReleasedCallback([&](uint32_t key) { root.setKeyboardKey(translate_key(key, false), 0, false); }); // create tables ControlGrid table_left(&root, 4); table_left.setAlign(Control::AlignLeft | Control::AlignTop); table_left.setMargin(96.0f, 0.0f, 0.0f, 64.0f); for(uint32_t i = 0, j = 0; i < 16; i++) { ControlGrid table(&table_left, 4); table.setAlign(Control::AlignCenter); table.setMargin(2.0f); for(uint32_t k = 0; k < 16; k++) { ControlText text(&table, String::format("%u", j++)); text.setAlign(Control::AlignCenter); text.setFontSize(12); text.setMargin(2.0f); } } ControlGrid table_right(&root, 8, 4.0f, 4.0f); table_right.setAlign(Control::AlignRight | Control::AlignTop); table_right.setMargin(0.0f, 96.0f, 0.0f, 64.0f); for(uint32_t i = 0; i < 64; i++) { ControlButton button(&table_right, String::format("%u", i)); button.setAlign(Control::AlignCenter); button.setSize(24.0f, 0.0f); } // create left area ControlArea left_area(&root); left_area.setAlign(Control::AlignLeft | Control::AlignBottom); left_area.setMargin(96.0f, 0.0f, 64.0f, 0.0f); left_area.setSize(384.0f, 256.0f); // create left rect texture constexpr uint32_t rect_size = 1024; Image rect_image(Image::Type2D, FormatRGBAu8n, Tellusim::Size(rect_size, rect_size)); ImageSampler rect_sampler(rect_image); for(uint32_t y = 0; y < rect_size; y++) { for(uint32_t x = 0; x < rect_size; x++) { uint32_t c = 0xaf - ((x & 0x7f) ^ (y & 0x7f)); rect_sampler.set2D(x, y, ImageColor(c, c, c, 255u)); } } Texture rect_texture = device.createTexture(rect_image); // create left rect ControlRect left_rect(&left_area); left_rect.setMode(CanvasElement::ModeTexture); left_rect.setTexture(rect_texture); left_area.setValue((rect_size - 384.0f + 14.0f) * 0.5f, (rect_size - 256.0f + 14.0f) * 0.5f); // create right area ControlArea right_area(&root, false, true); right_area.setAlign(Control::AlignRight | Control::AlignBottom); right_area.setMargin(0.0f, 96.0f, 64.0f, 0.0f); right_area.setSize(384.0f, 256.0f); right_area.setValue(0.0f, 1e8f); // create right tree texture constexpr uint32_t tree_size = 16; constexpr uint32_t tree_layout = 16; Image tree_image(Image::Type2D, FormatRGBAu8n, Tellusim::Size(tree_size, tree_size * tree_layout)); ImageSampler tree_sampler(tree_image); for(uint32_t i = 0; i < tree_layout; i++) { uint32_t c = 255 - 255 * i / (tree_layout - 1); for(uint32_t y = 0; y < tree_size; y++) { for(uint32_t x = 0; x < tree_size; x++) { tree_sampler.set2D(x, tree_size * i + y, ImageColor(c, c, c, 255u)); } } } Texture tree_texture = device.createTexture(tree_image); // create right tree ControlTree tree(&right_area); tree.setAlign(Control::AlignExpand); tree.setTexture(tree_texture, tree_layout); uint32_t root_item = tree.addItem("root"); tree.addItems({ "first", "second", "third" }, root_item); tree.addItems({ "fifth" , "sixth" }, tree.addItem("fourth", root_item)); tree.addItem("seveth", root_item); // create items constexpr uint32_t num_items = 256; for(uint32_t i = 0; i < num_items; i++) { float32_t k = (float32_t)i / (num_items - 1); uint32_t item = tree.addItem(String::format("item %u", i), root_item); tree.setItemColor(item, Color(0.5f + k * 0.5f, 1.0f - k * 0.5f, 1.0f, 1.0f)); tree.setItemTexture(item, i % tree_layout); if((i & 0x0f) == 0) root_item = item; } tree.setChangedCallback([](ControlTree tree, uint32_t item) { TS_LOGF(Message, "Tree changed %u\n", item); }); tree.setDraggedCallback([](ControlTree tree, uint32_t item) -> bool { TS_LOGF(Message, "Tree dragged %u\n", item); return tree.getItemText(item).begins("item"); }); tree.setDroppedCallback([](ControlTree tree, uint32_t item) { TS_LOGF(Message, "Tree dropped %u\n", item); if(tree.getItemText(item).begins("item")) tree.addItemChildren(item, tree.getSelectedItems()); }); tree.setClickedCallback([](ControlTree tree, uint32_t item) { TS_LOGF(Message, "Tree clicked %u\n", item); }); tree.setClicked2Callback([](ControlTree tree, uint32_t item) { TS_LOGF(Message, "Tree clicked2 %u\n", item); tree.switchItemExpanded(item); }); tree.setExpandedCallback([](ControlTree tree, uint32_t item) { TS_LOGF(Message, "Tree expanded %u\n", item); }); // create controls ControlDialog dialog(&root, 1, 0.0f, 8.0f); dialog.setAlign(Control::AlignCenter); ControlSplit split(&dialog, 1.0f); split.setAlign(Control::AlignExpand); ControlGrid split_left(&split, 1, 0.0f, 8.0f); split_left.setAlign(Control::AlignExpand); Control split_right(&split); ControlGroup group(&split_left, "Color", 1, 0.0f, 8.0f); group.setClickedCallback([](ControlGroup group) { TS_LOGF(Message, "Group clicked %u\n", group.isExpanded()); }); group.setAlign(Control::AlignExpandX); group.setFoldable(true); ControlSlider rgba[4] = { ControlSlider(&group, "Red", 2, 0.25f, 0.0f, 1.0f), ControlSlider(&group, "Green", 2, 0.25f, 0.0f, 1.0f), ControlSlider(&group, "Blue", 2, 0.25f, 0.0f, 1.0f), ControlSlider(&group, "Alpha", 2, 1.00f, 0.0f, 1.0f), }; rgba[0].setFontColor(Color(1.0f, 0.2f, 0.2f, 1.0f)); rgba[1].setFontColor(Color(0.2f, 1.0f, 0.2f, 1.0f)); rgba[2].setFontColor(Color(0.2f, 0.2f, 1.0f, 1.0f)); rgba[3].setFontColor(Color(1.0f, 1.0f, 1.0f, 1.0f)); rgba[0].setFontAlign(Control::AlignLeft); rgba[1].setFontAlign(Control::AlignCenter); rgba[2].setFontAlign(Control::AlignRight); rgba[3].setFontAlign(Control::AlignLeft); rgba[0].getFontStyle().size += 0; rgba[1].getFontStyle().size += 4; rgba[2].getFontStyle().size += 8; rgba[3].getFontStyle().size += 0; for(uint32_t i = 0; i < 4; i++) { rgba[i].setClickedCallback([](ControlSlider slider) { TS_LOGF(Message, "Slider clicked %f\n", slider.getValue()); }); rgba[i].setChangedCallback([](ControlSlider slider) { TS_LOGF(Message, "Slider changed %f\n", slider.getValue()); }); rgba[i].setAlign(Control::AlignExpandX); } ControlButton button(&split_left, "Reset"); button.setClickedCallback([&](ControlButton button) { rgba[0].setValue(0.25f); rgba[1].setValue(0.25f); rgba[2].setValue(0.25f); rgba[3].setValue(1.0f); TS_LOG(Message, "Button clicked\n"); }); button.setAlign(Control::AlignExpandX); ControlGrid edit_grid(&split_left, 3, 4.0f, 0.0f); edit_grid.setAlign(Control::AlignExpandX); ControlEdit edit(&edit_grid, "ControlEdit"); edit.setChangedCallback([&](ControlEdit edit) { TS_LOGF(Message, "Edit changed \"%s\" %u %u\n", edit.getText().get(), edit.getCurrentIndex(), edit.getSelectionIndex()); }); edit.setClickedCallback([&](ControlEdit edit) { TS_LOGF(Message, "Edit clicked \"%s\" %u %u\n", edit.getText().get(), edit.getCurrentIndex(), edit.getSelectionIndex()); }); edit.setReturnedCallback([&](ControlEdit edit) { TS_LOGF(Message, "Edit returned \"%s\" %u %u\n", edit.getText().get(), edit.getCurrentIndex(), edit.getSelectionIndex()); }); edit.setAlign(Control::AlignExpandX); ControlCombo edit_mode(&edit_grid, { "Text", "Pwd", "Float", "SInt", "UInt", "Hex" }); edit_mode.setChangedCallback([&](ControlCombo combo) { edit.setEditMode((ControlEdit::EditMode)combo.getCurrentIndex()); }); edit_mode.setAlign(Control::AlignRight | Control::AlignCenterY); ControlCombo edit_align(&edit_grid, { "Left", "Center", "Right" }); edit_align.setChangedCallback([&](ControlCombo combo) { Control::Align aligngs[] = { Control::AlignLeft, Control::AlignCenterX, Control::AlignRight }; edit.setFontAlign(aligngs[combo.getCurrentIndex()]); }); edit_align.setAlign(Control::AlignRight | Control::AlignCenterY); ControlScroll scroll(&split_left, (16.0f - 4.0f) / 2.0f, 4.0f, 16.0f); scroll.setClickedCallback([&](ControlScroll scroll) { TS_LOGF(Message, "Scroll clicked %f\n", scroll.getValue()); edit.setSelection(true); }); scroll.setChangedCallback([&](ControlScroll scroll) { TS_LOGF(Message, "Scroll changed %f\n", scroll.getValue()); edit.setText(String::fromf64(scroll.getValue(), 2).get()); }); scroll.setAlign(Control::AlignExpandX); scroll.getFontStyle().size += 2; scroll.setStep(0.1f); ControlGrid grid(&split_left, 3, 4.0f, 8.0f); grid.setAlign(Control::AlignBottom | Control::AlignExpand); ControlCheck check(&grid, "Check"); check.setClickedCallback([](ControlCheck check) { TS_LOGF(Message, "Check clicked %u\n", check.isChecked()); }); check.setAlign(Control::AlignBottom | Control::AlignCenterX | Control::AlignExpand); check.getFontStyle().size += 8; check = ControlCheck(&grid, "Check", true); check.setClickedCallback([](ControlCheck check) { TS_LOGF(Message, "Check clicked %u\n", check.isChecked()); }); check.setAlign(Control::AlignCenterX | Control::AlignExpandX); check.getFontStyle().size += 8; check = ControlCheck(&grid, "Check"); check.setClickedCallback([](ControlCheck check) { TS_LOGF(Message, "Check clicked %u\n", check.isChecked()); }); check.setAlign(Control::AlignCenterX | Control::AlignExpandX); check.setFontAlign(Control::AlignLeft); ControlCombo combo(&grid, { "Combo", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth" }); combo.setChangedCallback([](ControlCombo combo) { TS_LOGF(Message, "Combo changed %u\n", combo.getCurrentIndex()); }); combo.setAlign(Control::AlignCenterX | Control::AlignExpandX); combo.getFontStyle().size += 2; combo = ControlCombo(&grid, { "Combo", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth" }, 3); combo.setChangedCallback([](ControlCombo combo) { TS_LOGF(Message, "Combo changed %u\n", combo.getCurrentIndex()); }); combo.setAlign(Control::AlignCenterX | Control::AlignExpandX); combo.getFontStyle().size += 2; combo = ControlCombo(&grid, { "Combo", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth" }); combo.setChangedCallback([](ControlCombo combo) { TS_LOGF(Message, "Combo changed %u\n", combo.getCurrentIndex()); }); combo.setAlign(Control::AlignCenterX | Control::AlignExpandX); combo.setFontAlign(Control::AlignLeft); // create buttons ControlButton buttons[] = { ControlButton(&root, "LB"), ControlButton(&root, "RB"), ControlButton(&root, "LT"), ControlButton(&root, "RT"), ControlButton(&root, "LC"), ControlButton(&root, "RC"), ControlButton(&root, "CB"), ControlButton(&root, "CT"), ControlButton(&root, "CC"), }; buttons[0].setAlign(Control::AlignLeft | Control::AlignBottom); buttons[1].setAlign(Control::AlignRight | Control::AlignBottom); buttons[2].setAlign(Control::AlignLeft | Control::AlignTop); buttons[3].setAlign(Control::AlignRight | Control::AlignTop); buttons[4].setAlign(Control::AlignLeft | Control::AlignCenterY); buttons[5].setAlign(Control::AlignRight | Control::AlignCenterY); buttons[6].setAlign(Control::AlignCenterX | Control::AlignBottom); buttons[7].setAlign(Control::AlignCenterX | Control::AlignTop); buttons[8].setAlign(Control::AlignCenterX | Control::AlignCenterY); buttons[0].setMargin(32.0f, 0.0f, 32.0f, 0.0f); buttons[1].setMargin( 0.0f, 32.0f, 32.0f, 0.0f); buttons[2].setMargin(32.0f, 0.0f, 0.0f, 32.0f); buttons[3].setMargin( 0.0f, 32.0f, 0.0f, 32.0f); buttons[4].setMargin(16.0f, 0.0f, 0.0f, 0.0f); buttons[5].setMargin( 0.0f, 16.0f, 0.0f, 0.0f); buttons[6].setMargin( 0.0f, 0.0f, 16.0f, 0.0f); buttons[7].setMargin( 0.0f, 0.0f, 0.0f, 16.0f); buttons[8].setPosition(0.0f, 256.0f); for(uint32_t i = 0; i < TS_COUNTOF(buttons); i++) { buttons[i].setClickedCallback([&](ControlButton button) { dialog.setPosition(Vector3f(0.0f)); dialog.setAlign(button.getAlign()); TS_LOGF(Message, "%s clicked\n", button.getText().get()); }); buttons[i].setFontAlign(buttons[i].getAlign()); buttons[i].setFontSize(24); buttons[i].setSize(40.0f, 40.0f); } // create target Target target = device.createTarget(window); // main loop DECLARE_GLOBAL window.run([&]() -> bool { DECLARE_COMMON Window::update(); if(!window.render()) return false; // window size float32_t height = 720.0f; float32_t width = Tellusim::floor(height * (float32_t)window.getWidth() / (float32_t)window.getHeight()); float32_t mouse_x = width * (float32_t)window.getMouseX() / (float32_t)window.getWidth(); float32_t mouse_y = height * (float32_t)window.getMouseY() / (float32_t)window.getHeight(); // update controls root.setViewport(width, height); if(!pause) root.setMouse(mouse_x, mouse_y, translate_button(window.getMouseButtons())); while(root.update(canvas.getScale(target))) { } // update mouse cursor Window::Cursor cursor = Window::CursorArrow; if(dialog.hasResizeAligns(Control::AlignBottom | Control::AlignLeft)) cursor = Window::CursorAll; else if(dialog.hasResizeAligns(Control::AlignBottom | Control::AlignRight)) cursor = Window::CursorAll; else if(dialog.hasResizeAligns(Control::AlignTop | Control::AlignLeft)) cursor = Window::CursorAll; else if(dialog.hasResizeAligns(Control::AlignTop | Control::AlignRight)) cursor = Window::CursorAll; else if(dialog.hasResizeAlign(Control::AlignLeft)) cursor = Window::CursorLeft; else if(dialog.hasResizeAlign(Control::AlignRight)) cursor = Window::CursorRight; else if(dialog.hasResizeAlign(Control::AlignBottom)) cursor = Window::CursorBottom; else if(dialog.hasResizeAlign(Control::AlignTop)) cursor = Window::CursorTop; if(window.getMouseCursor() != cursor) window.setMouseCursor(cursor); // create canvas canvas.create(device, target); canvas.setColor(Color(1.0f, 1.0f, 1.0f, rgba[3].getValuef32())); // window target target.setClearColor(rgba[0].getValuef32(), rgba[1].getValuef32(), rgba[2].getValuef32(), 1.0f); target.begin(); { // create command list Command command = device.createCommand(target); // draw canvas canvas.draw(command, target); } target.end(); if(!window.present()) return false; if(!device.check()) return false; return true; }); // finish context window.finish(); return 0; }