| 1 | /* |
| 2 | src/example1.cpp -- C++ version of an example application that shows |
| 3 | how to use the various widget classes. For a Python implementation, see |
| 4 | '../python/example1.py'. |
| 5 | |
| 6 | NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. |
| 7 | The widget drawing code is based on the NanoVG demo application |
| 8 | by Mikko Mononen. |
| 9 | |
| 10 | All rights reserved. Use of this source code is governed by a |
| 11 | BSD-style license that can be found in the LICENSE.txt file. |
| 12 | */ |
| 13 | |
| 14 | #include <nanogui/opengl.h> |
| 15 | #include <nanogui/glutil.h> |
| 16 | #include <nanogui/screen.h> |
| 17 | #include <nanogui/window.h> |
| 18 | #include <nanogui/layout.h> |
| 19 | #include <nanogui/label.h> |
| 20 | #include <nanogui/checkbox.h> |
| 21 | #include <nanogui/button.h> |
| 22 | #include <nanogui/toolbutton.h> |
| 23 | #include <nanogui/popupbutton.h> |
| 24 | #include <nanogui/combobox.h> |
| 25 | #include <nanogui/progressbar.h> |
| 26 | #include <nanogui/entypo.h> |
| 27 | #include <nanogui/messagedialog.h> |
| 28 | #include <nanogui/textbox.h> |
| 29 | #include <nanogui/slider.h> |
| 30 | #include <nanogui/imagepanel.h> |
| 31 | #include <nanogui/imageview.h> |
| 32 | #include <nanogui/vscrollpanel.h> |
| 33 | #include <nanogui/colorwheel.h> |
| 34 | #include <nanogui/colorpicker.h> |
| 35 | #include <nanogui/graph.h> |
| 36 | #include <nanogui/tabwidget.h> |
| 37 | #include <iostream> |
| 38 | #include <string> |
| 39 | |
| 40 | // Includes for the GLTexture class. |
| 41 | #include <cstdint> |
| 42 | #include <memory> |
| 43 | #include <utility> |
| 44 | |
| 45 | #if defined(__GNUC__) |
| 46 | # pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
| 47 | #endif |
| 48 | #if defined(_WIN32) |
| 49 | # pragma warning(push) |
| 50 | # pragma warning(disable: 4457 4456 4005 4312) |
| 51 | #endif |
| 52 | |
| 53 | #define STB_IMAGE_STATIC |
| 54 | #define STB_IMAGE_IMPLEMENTATION |
| 55 | #include <stb_image.h> |
| 56 | |
| 57 | #if defined(_WIN32) |
| 58 | # pragma warning(pop) |
| 59 | #endif |
| 60 | #if defined(_WIN32) |
| 61 | # if defined(APIENTRY) |
| 62 | # undef APIENTRY |
| 63 | # endif |
| 64 | # include <windows.h> |
| 65 | #endif |
| 66 | |
| 67 | using std::cout; |
| 68 | using std::cerr; |
| 69 | using std::endl; |
| 70 | using std::string; |
| 71 | using std::vector; |
| 72 | using std::pair; |
| 73 | using std::to_string; |
| 74 | |
| 75 | class GLTexture { |
| 76 | public: |
| 77 | using handleType = std::unique_ptr<uint8_t[], void(*)(void*)>; |
| 78 | GLTexture() = default; |
| 79 | GLTexture(const std::string& textureName) |
| 80 | : mTextureName(textureName), mTextureId(0) {} |
| 81 | |
| 82 | GLTexture(const std::string& textureName, GLint textureId) |
| 83 | : mTextureName(textureName), mTextureId(textureId) {} |
| 84 | |
| 85 | GLTexture(const GLTexture& other) = delete; |
| 86 | GLTexture(GLTexture&& other) noexcept |
| 87 | : mTextureName(std::move(other.mTextureName)), |
| 88 | mTextureId(other.mTextureId) { |
| 89 | other.mTextureId = 0; |
| 90 | } |
| 91 | GLTexture& operator=(const GLTexture& other) = delete; |
| 92 | GLTexture& operator=(GLTexture&& other) noexcept { |
| 93 | mTextureName = std::move(other.mTextureName); |
| 94 | std::swap(mTextureId, other.mTextureId); |
| 95 | return *this; |
| 96 | } |
| 97 | ~GLTexture() noexcept { |
| 98 | if (mTextureId) |
| 99 | glDeleteTextures(1, &mTextureId); |
| 100 | } |
| 101 | |
| 102 | GLuint texture() const { return mTextureId; } |
| 103 | const std::string& textureName() const { return mTextureName; } |
| 104 | |
| 105 | /** |
| 106 | * Load a file in memory and create an OpenGL texture. |
| 107 | * Returns a handle type (an std::unique_ptr) to the loaded pixels. |
| 108 | */ |
| 109 | handleType load(const std::string& fileName) { |
| 110 | if (mTextureId) { |
| 111 | glDeleteTextures(1, &mTextureId); |
| 112 | mTextureId = 0; |
| 113 | } |
| 114 | int force_channels = 0; |
| 115 | int w, h, n; |
| 116 | handleType textureData(stbi_load(fileName.c_str(), &w, &h, &n, force_channels), stbi_image_free); |
| 117 | if (!textureData) |
| 118 | throw std::invalid_argument("Could not load texture data from file " + fileName); |
| 119 | glGenTextures(1, &mTextureId); |
| 120 | glBindTexture(GL_TEXTURE_2D, mTextureId); |
| 121 | GLint internalFormat; |
| 122 | GLint format; |
| 123 | switch (n) { |
| 124 | case 1: internalFormat = GL_R8; format = GL_RED; break; |
| 125 | case 2: internalFormat = GL_RG8; format = GL_RG; break; |
| 126 | case 3: internalFormat = GL_RGB8; format = GL_RGB; break; |
| 127 | case 4: internalFormat = GL_RGBA8; format = GL_RGBA; break; |
| 128 | default: internalFormat = 0; format = 0; break; |
| 129 | } |
| 130 | glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, textureData.get()); |
| 131 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 134 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 135 | return textureData; |
| 136 | } |
| 137 | |
| 138 | private: |
| 139 | std::string mTextureName; |
| 140 | GLuint mTextureId; |
| 141 | }; |
| 142 | |
| 143 | class ExampleApplication : public nanogui::Screen { |
| 144 | public: |
| 145 | ExampleApplication() : nanogui::Screen(Eigen::Vector2i(1024, 768), "NanoGUI Test" ) { |
| 146 | using namespace nanogui; |
| 147 | |
| 148 | Window *window = new Window(this, "Button demo" ); |
| 149 | window->setPosition(Vector2i(15, 15)); |
| 150 | window->setLayout(new GroupLayout()); |
| 151 | |
| 152 | /* No need to store a pointer, the data structure will be automatically |
| 153 | freed when the parent window is deleted */ |
| 154 | new Label(window, "Push buttons" , "sans-bold" ); |
| 155 | |
| 156 | Button *b = new Button(window, "Plain button" ); |
| 157 | b->setCallback([] { cout << "pushed!" << endl; }); |
| 158 | b->setTooltip("short tooltip" ); |
| 159 | |
| 160 | /* Alternative construction notation using variadic template */ |
| 161 | b = window->add<Button>("Styled" , ENTYPO_ICON_ROCKET); |
| 162 | b->setBackgroundColor(Color(0, 0, 255, 25)); |
| 163 | b->setCallback([] { cout << "pushed!" << endl; }); |
| 164 | b->setTooltip("This button has a fairly long tooltip. It is so long, in " |
| 165 | "fact, that the shown text will span several lines." ); |
| 166 | |
| 167 | new Label(window, "Toggle buttons" , "sans-bold" ); |
| 168 | b = new Button(window, "Toggle me" ); |
| 169 | b->setFlags(Button::ToggleButton); |
| 170 | b->setChangeCallback([](bool state) { cout << "Toggle button state: " << state << endl; }); |
| 171 | |
| 172 | new Label(window, "Radio buttons" , "sans-bold" ); |
| 173 | b = new Button(window, "Radio button 1" ); |
| 174 | b->setFlags(Button::RadioButton); |
| 175 | b = new Button(window, "Radio button 2" ); |
| 176 | b->setFlags(Button::RadioButton); |
| 177 | |
| 178 | new Label(window, "A tool palette" , "sans-bold" ); |
| 179 | Widget *tools = new Widget(window); |
| 180 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
| 181 | Alignment::Middle, 0, 6)); |
| 182 | |
| 183 | b = new ToolButton(tools, ENTYPO_ICON_CLOUD); |
| 184 | b = new ToolButton(tools, ENTYPO_ICON_CONTROLLER_FAST_FORWARD); |
| 185 | b = new ToolButton(tools, ENTYPO_ICON_COMPASS); |
| 186 | b = new ToolButton(tools, ENTYPO_ICON_INSTALL); |
| 187 | |
| 188 | new Label(window, "Popup buttons" , "sans-bold" ); |
| 189 | PopupButton * = new PopupButton(window, "Popup" , ENTYPO_ICON_EXPORT); |
| 190 | Popup * = popupBtn->popup(); |
| 191 | popup->setLayout(new GroupLayout()); |
| 192 | new Label(popup, "Arbitrary widgets can be placed here" ); |
| 193 | new CheckBox(popup, "A check box" ); |
| 194 | // popup right |
| 195 | popupBtn = new PopupButton(popup, "Recursive popup" , ENTYPO_ICON_FLASH); |
| 196 | Popup * = popupBtn->popup(); |
| 197 | popupRight->setLayout(new GroupLayout()); |
| 198 | new CheckBox(popupRight, "Another check box" ); |
| 199 | // popup left |
| 200 | popupBtn = new PopupButton(popup, "Recursive popup" , ENTYPO_ICON_FLASH); |
| 201 | popupBtn->setSide(Popup::Side::Left); |
| 202 | Popup * = popupBtn->popup(); |
| 203 | popupLeft->setLayout(new GroupLayout()); |
| 204 | new CheckBox(popupLeft, "Another check box" ); |
| 205 | |
| 206 | window = new Window(this, "Basic widgets" ); |
| 207 | window->setPosition(Vector2i(200, 15)); |
| 208 | window->setLayout(new GroupLayout()); |
| 209 | |
| 210 | new Label(window, "Message dialog" , "sans-bold" ); |
| 211 | tools = new Widget(window); |
| 212 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
| 213 | Alignment::Middle, 0, 6)); |
| 214 | b = new Button(tools, "Info" ); |
| 215 | b->setCallback([&] { |
| 216 | auto dlg = new MessageDialog(this, MessageDialog::Type::Information, "Title" , "This is an information message" ); |
| 217 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
| 218 | }); |
| 219 | b = new Button(tools, "Warn" ); |
| 220 | b->setCallback([&] { |
| 221 | auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title" , "This is a warning message" ); |
| 222 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
| 223 | }); |
| 224 | b = new Button(tools, "Ask" ); |
| 225 | b->setCallback([&] { |
| 226 | auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title" , "This is a question message" , "Yes" , "No" , true); |
| 227 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
| 228 | }); |
| 229 | |
| 230 | vector<pair<int, string>> |
| 231 | icons = loadImageDirectory(mNVGContext, "icons" ); |
| 232 | #if defined(_WIN32) |
| 233 | string resourcesFolderPath("../resources/" ); |
| 234 | #else |
| 235 | string resourcesFolderPath("./" ); |
| 236 | #endif |
| 237 | |
| 238 | new Label(window, "Image panel & scroll panel" , "sans-bold" ); |
| 239 | PopupButton *imagePanelBtn = new PopupButton(window, "Image Panel" ); |
| 240 | imagePanelBtn->setIcon(ENTYPO_ICON_FOLDER); |
| 241 | popup = imagePanelBtn->popup(); |
| 242 | VScrollPanel *vscroll = new VScrollPanel(popup); |
| 243 | ImagePanel *imgPanel = new ImagePanel(vscroll); |
| 244 | imgPanel->setImages(icons); |
| 245 | popup->setFixedSize(Vector2i(245, 150)); |
| 246 | |
| 247 | auto imageWindow = new Window(this, "Selected image" ); |
| 248 | imageWindow->setPosition(Vector2i(710, 15)); |
| 249 | imageWindow->setLayout(new GroupLayout()); |
| 250 | |
| 251 | // Load all of the images by creating a GLTexture object and saving the pixel data. |
| 252 | for (auto& icon : icons) { |
| 253 | GLTexture texture(icon.second); |
| 254 | auto data = texture.load(resourcesFolderPath + icon.second + ".png" ); |
| 255 | mImagesData.emplace_back(std::move(texture), std::move(data)); |
| 256 | } |
| 257 | |
| 258 | // Set the first texture |
| 259 | auto imageView = new ImageView(imageWindow, mImagesData[0].first.texture()); |
| 260 | mCurrentImage = 0; |
| 261 | // Change the active textures. |
| 262 | imgPanel->setCallback([this, imageView](int i) { |
| 263 | imageView->bindImage(mImagesData[i].first.texture()); |
| 264 | mCurrentImage = i; |
| 265 | cout << "Selected item " << i << '\n'; |
| 266 | }); |
| 267 | imageView->setGridThreshold(20); |
| 268 | imageView->setPixelInfoThreshold(20); |
| 269 | imageView->setPixelInfoCallback( |
| 270 | [this, imageView](const Vector2i& index) -> pair<string, Color> { |
| 271 | auto& imageData = mImagesData[mCurrentImage].second; |
| 272 | auto& textureSize = imageView->imageSize(); |
| 273 | string stringData; |
| 274 | uint16_t channelSum = 0; |
| 275 | for (int i = 0; i != 4; ++i) { |
| 276 | auto& channelData = imageData[4*index.y()*textureSize.x() + 4*index.x() + i]; |
| 277 | channelSum += channelData; |
| 278 | stringData += (to_string(static_cast<int>(channelData)) + "\n" ); |
| 279 | } |
| 280 | float intensity = static_cast<float>(255 - (channelSum / 4)) / 255.0f; |
| 281 | float colorScale = intensity > 0.5f ? (intensity + 1) / 2 : intensity / 2; |
| 282 | Color textColor = Color(colorScale, 1.0f); |
| 283 | return { stringData, textColor }; |
| 284 | }); |
| 285 | |
| 286 | new Label(window, "File dialog" , "sans-bold" ); |
| 287 | tools = new Widget(window); |
| 288 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
| 289 | Alignment::Middle, 0, 6)); |
| 290 | b = new Button(tools, "Open" ); |
| 291 | b->setCallback([&] { |
| 292 | cout << "File dialog result: " << file_dialog( |
| 293 | { {"png" , "Portable Network Graphics" }, {"txt" , "Text file" } }, false) << endl; |
| 294 | }); |
| 295 | b = new Button(tools, "Save" ); |
| 296 | b->setCallback([&] { |
| 297 | cout << "File dialog result: " << file_dialog( |
| 298 | { {"png" , "Portable Network Graphics" }, {"txt" , "Text file" } }, true) << endl; |
| 299 | }); |
| 300 | |
| 301 | new Label(window, "Combo box" , "sans-bold" ); |
| 302 | new ComboBox(window, { "Combo box item 1" , "Combo box item 2" , "Combo box item 3" }); |
| 303 | new Label(window, "Check box" , "sans-bold" ); |
| 304 | CheckBox *cb = new CheckBox(window, "Flag 1" , |
| 305 | [](bool state) { cout << "Check box 1 state: " << state << endl; } |
| 306 | ); |
| 307 | cb->setChecked(true); |
| 308 | cb = new CheckBox(window, "Flag 2" , |
| 309 | [](bool state) { cout << "Check box 2 state: " << state << endl; } |
| 310 | ); |
| 311 | new Label(window, "Progress bar" , "sans-bold" ); |
| 312 | mProgress = new ProgressBar(window); |
| 313 | |
| 314 | new Label(window, "Slider and text box" , "sans-bold" ); |
| 315 | |
| 316 | Widget *panel = new Widget(window); |
| 317 | panel->setLayout(new BoxLayout(Orientation::Horizontal, |
| 318 | Alignment::Middle, 0, 20)); |
| 319 | |
| 320 | Slider *slider = new Slider(panel); |
| 321 | slider->setValue(0.5f); |
| 322 | slider->setFixedWidth(80); |
| 323 | |
| 324 | TextBox *textBox = new TextBox(panel); |
| 325 | textBox->setFixedSize(Vector2i(60, 25)); |
| 326 | textBox->setValue("50" ); |
| 327 | textBox->setUnits("%" ); |
| 328 | slider->setCallback([textBox](float value) { |
| 329 | textBox->setValue(std::to_string((int) (value * 100))); |
| 330 | }); |
| 331 | slider->setFinalCallback([&](float value) { |
| 332 | cout << "Final slider value: " << (int) (value * 100) << endl; |
| 333 | }); |
| 334 | textBox->setFixedSize(Vector2i(60,25)); |
| 335 | textBox->setFontSize(20); |
| 336 | textBox->setAlignment(TextBox::Alignment::Right); |
| 337 | |
| 338 | window = new Window(this, "Misc. widgets" ); |
| 339 | window->setPosition(Vector2i(425,15)); |
| 340 | window->setLayout(new GroupLayout()); |
| 341 | |
| 342 | TabWidget* tabWidget = window->add<TabWidget>(); |
| 343 | |
| 344 | Widget* layer = tabWidget->createTab("Color Wheel" ); |
| 345 | layer->setLayout(new GroupLayout()); |
| 346 | |
| 347 | // Use overloaded variadic add to fill the tab widget with Different tabs. |
| 348 | layer->add<Label>("Color wheel widget" , "sans-bold" ); |
| 349 | layer->add<ColorWheel>(); |
| 350 | |
| 351 | layer = tabWidget->createTab("Function Graph" ); |
| 352 | layer->setLayout(new GroupLayout()); |
| 353 | |
| 354 | layer->add<Label>("Function graph widget" , "sans-bold" ); |
| 355 | |
| 356 | Graph *graph = layer->add<Graph>("Some Function" ); |
| 357 | |
| 358 | graph->setHeader("E = 2.35e-3" ); |
| 359 | graph->setFooter("Iteration 89" ); |
| 360 | VectorXf &func = graph->values(); |
| 361 | func.resize(100); |
| 362 | for (int i = 0; i < 100; ++i) |
| 363 | func[i] = 0.5f * (0.5f * std::sin(i / 10.f) + |
| 364 | 0.5f * std::cos(i / 23.f) + 1); |
| 365 | |
| 366 | // Dummy tab used to represent the last tab button. |
| 367 | tabWidget->createTab("+" ); |
| 368 | |
| 369 | // A simple counter. |
| 370 | int counter = 1; |
| 371 | tabWidget->setCallback([tabWidget, this, counter] (int index) mutable { |
| 372 | if (index == (tabWidget->tabCount()-1)) { |
| 373 | // When the "+" tab has been clicked, simply add a new tab. |
| 374 | string tabName = "Dynamic " + to_string(counter); |
| 375 | Widget* layerDyn = tabWidget->createTab(index, tabName); |
| 376 | layerDyn->setLayout(new GroupLayout()); |
| 377 | layerDyn->add<Label>("Function graph widget" , "sans-bold" ); |
| 378 | Graph *graphDyn = layerDyn->add<Graph>("Dynamic function" ); |
| 379 | |
| 380 | graphDyn->setHeader("E = 2.35e-3" ); |
| 381 | graphDyn->setFooter("Iteration " + to_string(index*counter)); |
| 382 | VectorXf &funcDyn = graphDyn->values(); |
| 383 | funcDyn.resize(100); |
| 384 | for (int i = 0; i < 100; ++i) |
| 385 | funcDyn[i] = 0.5f * |
| 386 | std::abs((0.5f * std::sin(i / 10.f + counter) + |
| 387 | 0.5f * std::cos(i / 23.f + 1 + counter))); |
| 388 | ++counter; |
| 389 | // We must invoke perform layout from the screen instance to keep everything in order. |
| 390 | // This is essential when creating tabs dynamically. |
| 391 | performLayout(); |
| 392 | // Ensure that the newly added header is visible on screen |
| 393 | tabWidget->ensureTabVisible(index); |
| 394 | |
| 395 | } |
| 396 | }); |
| 397 | tabWidget->setActiveTab(0); |
| 398 | |
| 399 | // A button to go back to the first tab and scroll the window. |
| 400 | panel = window->add<Widget>(); |
| 401 | panel->add<Label>("Jump to tab: " ); |
| 402 | panel->setLayout(new BoxLayout(Orientation::Horizontal, |
| 403 | Alignment::Middle, 0, 6)); |
| 404 | |
| 405 | auto ib = panel->add<IntBox<int>>(); |
| 406 | ib->setEditable(true); |
| 407 | |
| 408 | b = panel->add<Button>("" , ENTYPO_ICON_FORWARD); |
| 409 | b->setFixedSize(Vector2i(22, 22)); |
| 410 | ib->setFixedHeight(22); |
| 411 | b->setCallback([tabWidget, ib] { |
| 412 | int value = ib->value(); |
| 413 | if (value >= 0 && value < tabWidget->tabCount()) { |
| 414 | tabWidget->setActiveTab(value); |
| 415 | tabWidget->ensureTabVisible(value); |
| 416 | } |
| 417 | }); |
| 418 | |
| 419 | window = new Window(this, "Grid of small widgets" ); |
| 420 | window->setPosition(Vector2i(425, 300)); |
| 421 | GridLayout *layout = |
| 422 | new GridLayout(Orientation::Horizontal, 2, |
| 423 | Alignment::Middle, 15, 5); |
| 424 | layout->setColAlignment( |
| 425 | { Alignment::Maximum, Alignment::Fill }); |
| 426 | layout->setSpacing(0, 10); |
| 427 | window->setLayout(layout); |
| 428 | |
| 429 | /* FP widget */ { |
| 430 | new Label(window, "Floating point :" , "sans-bold" ); |
| 431 | textBox = new TextBox(window); |
| 432 | textBox->setEditable(true); |
| 433 | textBox->setFixedSize(Vector2i(100, 20)); |
| 434 | textBox->setValue("50" ); |
| 435 | textBox->setUnits("GiB" ); |
| 436 | textBox->setDefaultValue("0.0" ); |
| 437 | textBox->setFontSize(16); |
| 438 | textBox->setFormat("[-]?[0-9]*\\.?[0-9]+" ); |
| 439 | } |
| 440 | |
| 441 | /* Positive integer widget */ { |
| 442 | new Label(window, "Positive integer :" , "sans-bold" ); |
| 443 | auto intBox = new IntBox<int>(window); |
| 444 | intBox->setEditable(true); |
| 445 | intBox->setFixedSize(Vector2i(100, 20)); |
| 446 | intBox->setValue(50); |
| 447 | intBox->setUnits("Mhz" ); |
| 448 | intBox->setDefaultValue("0" ); |
| 449 | intBox->setFontSize(16); |
| 450 | intBox->setFormat("[1-9][0-9]*" ); |
| 451 | intBox->setSpinnable(true); |
| 452 | intBox->setMinValue(1); |
| 453 | intBox->setValueIncrement(2); |
| 454 | } |
| 455 | |
| 456 | /* Checkbox widget */ { |
| 457 | new Label(window, "Checkbox :" , "sans-bold" ); |
| 458 | |
| 459 | cb = new CheckBox(window, "Check me" ); |
| 460 | cb->setFontSize(16); |
| 461 | cb->setChecked(true); |
| 462 | } |
| 463 | |
| 464 | new Label(window, "Combo box :" , "sans-bold" ); |
| 465 | ComboBox *cobo = |
| 466 | new ComboBox(window, { "Item 1" , "Item 2" , "Item 3" }); |
| 467 | cobo->setFontSize(16); |
| 468 | cobo->setFixedSize(Vector2i(100,20)); |
| 469 | |
| 470 | new Label(window, "Color picker :" , "sans-bold" ); |
| 471 | auto cp = new ColorPicker(window, {255, 120, 0, 255}); |
| 472 | cp->setFixedSize({100, 20}); |
| 473 | cp->setFinalCallback([](const Color &c) { |
| 474 | std::cout << "ColorPicker Final Callback: [" |
| 475 | << c.r() << ", " |
| 476 | << c.g() << ", " |
| 477 | << c.b() << ", " |
| 478 | << c.w() << "]" << std::endl; |
| 479 | }); |
| 480 | // setup a fast callback for the color picker widget on a new window |
| 481 | // for demonstrative purposes |
| 482 | window = new Window(this, "Color Picker Fast Callback" ); |
| 483 | layout = |
| 484 | new GridLayout(Orientation::Horizontal, 2, |
| 485 | Alignment::Middle, 15, 5); |
| 486 | layout->setColAlignment( |
| 487 | { Alignment::Maximum, Alignment::Fill }); |
| 488 | layout->setSpacing(0, 10); |
| 489 | window->setLayout(layout); |
| 490 | window->setPosition(Vector2i(425, 500)); |
| 491 | new Label(window, "Combined: " ); |
| 492 | b = new Button(window, "ColorWheel" , ENTYPO_ICON_500PX); |
| 493 | new Label(window, "Red: " ); |
| 494 | auto redIntBox = new IntBox<int>(window); |
| 495 | redIntBox->setEditable(false); |
| 496 | new Label(window, "Green: " ); |
| 497 | auto greenIntBox = new IntBox<int>(window); |
| 498 | greenIntBox->setEditable(false); |
| 499 | new Label(window, "Blue: " ); |
| 500 | auto blueIntBox = new IntBox<int>(window); |
| 501 | blueIntBox->setEditable(false); |
| 502 | new Label(window, "Alpha: " ); |
| 503 | auto alphaIntBox = new IntBox<int>(window); |
| 504 | cp->setCallback([b,redIntBox,blueIntBox,greenIntBox,alphaIntBox](const Color &c) { |
| 505 | b->setBackgroundColor(c); |
| 506 | b->setTextColor(c.contrastingColor()); |
| 507 | int red = (int) (c.r() * 255.0f); |
| 508 | redIntBox->setValue(red); |
| 509 | int green = (int) (c.g() * 255.0f); |
| 510 | greenIntBox->setValue(green); |
| 511 | int blue = (int) (c.b() * 255.0f); |
| 512 | blueIntBox->setValue(blue); |
| 513 | int alpha = (int) (c.w() * 255.0f); |
| 514 | alphaIntBox->setValue(alpha); |
| 515 | |
| 516 | }); |
| 517 | |
| 518 | performLayout(); |
| 519 | |
| 520 | /* All NanoGUI widgets are initialized at this point. Now |
| 521 | create an OpenGL shader to draw the main window contents. |
| 522 | |
| 523 | NanoGUI comes with a simple Eigen-based wrapper around OpenGL 3, |
| 524 | which eliminates most of the tedious and error-prone shader and |
| 525 | buffer object management. |
| 526 | */ |
| 527 | |
| 528 | mShader.init( |
| 529 | /* An identifying name */ |
| 530 | "a_simple_shader" , |
| 531 | |
| 532 | /* Vertex shader */ |
| 533 | "#version 330\n" |
| 534 | "uniform mat4 modelViewProj;\n" |
| 535 | "in vec3 position;\n" |
| 536 | "void main() {\n" |
| 537 | " gl_Position = modelViewProj * vec4(position, 1.0);\n" |
| 538 | "}" , |
| 539 | |
| 540 | /* Fragment shader */ |
| 541 | "#version 330\n" |
| 542 | "out vec4 color;\n" |
| 543 | "uniform float intensity;\n" |
| 544 | "void main() {\n" |
| 545 | " color = vec4(vec3(intensity), 1.0);\n" |
| 546 | "}" |
| 547 | ); |
| 548 | |
| 549 | MatrixXu indices(3, 2); /* Draw 2 triangles */ |
| 550 | indices.col(0) << 0, 1, 2; |
| 551 | indices.col(1) << 2, 3, 0; |
| 552 | |
| 553 | MatrixXf positions(3, 4); |
| 554 | positions.col(0) << -1, -1, 0; |
| 555 | positions.col(1) << 1, -1, 0; |
| 556 | positions.col(2) << 1, 1, 0; |
| 557 | positions.col(3) << -1, 1, 0; |
| 558 | |
| 559 | mShader.bind(); |
| 560 | mShader.uploadIndices(indices); |
| 561 | mShader.uploadAttrib("position" , positions); |
| 562 | mShader.setUniform("intensity" , 0.5f); |
| 563 | } |
| 564 | |
| 565 | ~ExampleApplication() { |
| 566 | mShader.free(); |
| 567 | } |
| 568 | |
| 569 | virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) { |
| 570 | if (Screen::keyboardEvent(key, scancode, action, modifiers)) |
| 571 | return true; |
| 572 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { |
| 573 | setVisible(false); |
| 574 | return true; |
| 575 | } |
| 576 | return false; |
| 577 | } |
| 578 | |
| 579 | virtual void draw(NVGcontext *ctx) { |
| 580 | /* Animate the scrollbar */ |
| 581 | mProgress->setValue(std::fmod((float) glfwGetTime() / 10, 1.0f)); |
| 582 | |
| 583 | /* Draw the user interface */ |
| 584 | Screen::draw(ctx); |
| 585 | } |
| 586 | |
| 587 | virtual void drawContents() { |
| 588 | using namespace nanogui; |
| 589 | |
| 590 | /* Draw the window contents using OpenGL */ |
| 591 | mShader.bind(); |
| 592 | |
| 593 | Matrix4f mvp; |
| 594 | mvp.setIdentity(); |
| 595 | mvp.topLeftCorner<3,3>() = Matrix3f(Eigen::AngleAxisf((float) glfwGetTime(), Vector3f::UnitZ())) * 0.25f; |
| 596 | |
| 597 | mvp.row(0) *= (float) mSize.y() / (float) mSize.x(); |
| 598 | |
| 599 | mShader.setUniform("modelViewProj" , mvp); |
| 600 | |
| 601 | /* Draw 2 triangles starting at index 0 */ |
| 602 | mShader.drawIndexed(GL_TRIANGLES, 0, 2); |
| 603 | } |
| 604 | private: |
| 605 | nanogui::ProgressBar *mProgress; |
| 606 | nanogui::GLShader mShader; |
| 607 | |
| 608 | using imagesDataType = vector<pair<GLTexture, GLTexture::handleType>>; |
| 609 | imagesDataType mImagesData; |
| 610 | int mCurrentImage; |
| 611 | }; |
| 612 | |
| 613 | int main(int /* argc */, char ** /* argv */) { |
| 614 | try { |
| 615 | nanogui::init(); |
| 616 | |
| 617 | /* scoped variables */ { |
| 618 | nanogui::ref<ExampleApplication> app = new ExampleApplication(); |
| 619 | app->drawAll(); |
| 620 | app->setVisible(true); |
| 621 | nanogui::mainloop(); |
| 622 | } |
| 623 | |
| 624 | nanogui::shutdown(); |
| 625 | } catch (const std::runtime_error &e) { |
| 626 | std::string error_msg = std::string("Caught a fatal error: " ) + std::string(e.what()); |
| 627 | #if defined(_WIN32) |
| 628 | MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); |
| 629 | #else |
| 630 | std::cerr << error_msg << endl; |
| 631 | #endif |
| 632 | return -1; |
| 633 | } |
| 634 | |
| 635 | return 0; |
| 636 | } |
| 637 | |