Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ resources/nanogui-entypo

compile_commands.json
.clangd
.cache
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ add_library(nanogui ${NANOGUI_LIBRARY_TYPE}
include/nanogui/toolbutton.h
include/nanogui/opengl.h
include/nanogui/nanogui.h
include/nanogui/borderlayout.h src/borderlayout.cpp
)

target_compile_definitions(nanogui
Expand Down Expand Up @@ -563,12 +564,14 @@ if (NANOGUI_BUILD_EXAMPLES)
add_executable(example2 src/example2.cpp)
add_executable(example3 src/example3.cpp)
add_executable(example4 src/example4.cpp)
add_executable(example5 src/example5.cpp)
add_executable(example_icons src/example_icons.cpp)

target_link_libraries(example1 nanogui)
target_link_libraries(example2 nanogui)
target_link_libraries(example3 nanogui ${NANOGUI_LIBS}) # For OpenGL
target_link_libraries(example4 nanogui)
target_link_libraries(example5 nanogui)
target_link_libraries(example_icons nanogui)

# Copy icons for example application
Expand Down
76 changes: 76 additions & 0 deletions include/nanogui/borderlayout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.

All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
/**
* \file nanogui/borderlayout.h
*
* \brief Defines the \ref nanogui::BorderLayout algorithm.
*/
#pragma once

#include <nanogui/layout.h>

NAMESPACE_BEGIN(nanogui)

/**
* \class BorderLayout borderlayout.h nanogui/borderlayout.h
*
* \brief Layout inspired by Java's BorderLayout
*
* Each widget will "carve" out a piece from the border of the parent.
* The center widget(s) will get the remaining area.
*
* Each child widget will be processed in order. Unlike in the Java version,
* it's allowed to have multiple widgets on the same side, they will be
* placed next to each other.
*
* It's also allowed to have multiple center widgets, they will be overlapped though.
*
* The layout needs to know the direction of the children, this is kept in a local
* hash map. After adding a child widget, the user also needs to call "set_side"
* to associate one of the borders/sides to the given widget. If no side is associated,
* it will be assumed to be center.
*/
class NANOGUI_EXPORT BorderLayout : public Layout
{
public:
/// The possible border/side positions of the children
enum Side {
North, ///< Place the child in the top of the available area
South, ///< Place the child in the bottom of the available area
West, ///< Place the child in the left of the available area
East, ///< Place the child in the right of the available area
Center, ///< Place the child in the available area
};

/**
* \brief Creates an instance of the layout
*
* \param margin
* Specifies the empty gap reserved at the edge of the parent widget
*
* \param spacing
* Specifies the empty gap reserved in between the children
*/
explicit BorderLayout(int margin = 0, int spacing = 0);

/// Associates a side with a child widget
void set_side(const Widget *widget, Side side);
/// Returns the side associated to the child widget
Side side(const Widget *widget) const;

Vector2i preferred_size(NVGcontext *ctx, const Widget *widget) const override;
void perform_layout(NVGcontext *ctx, Widget *widget) const override;

private:
std::unordered_map<const Widget *, Side> m_side;
int m_margin;
int m_spacing;
};

NAMESPACE_END(nanogui)
23 changes: 23 additions & 0 deletions include/nanogui/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ class NANOGUI_EXPORT Button : public Widget {
/// Sets whether or not this Button is currently pushed.
void set_pushed(bool pushed) { m_pushed = pushed; }

/// Whether or not this Button is flat.
bool flat() const { return m_flat; }
/// Sets whether or not this Button is flat (i.e. background and outline not rendered)
void set_flat(bool flat) { m_flat = flat; }

/// Horizontal padding on both sides
uint8_t horizontal_padding() const { return m_horizontal_padding; }
/// Sets the horizontal padding on both sides
void set_horizontal_padding(uint8_t horizontal_padding) { m_horizontal_padding = horizontal_padding; }

/// Vertical padding on both sides
uint8_t vertical_padding() const { return m_vertical_padding; }
/// Sets the vertical padding on both sides
void set_vertical_padding(uint8_t vertical_padding) { m_vertical_padding = vertical_padding; }

/// Return the push callback (for any type of button)
const std::function<void()> &callback() const { return m_callback; }
/// Set the push callback (for any type of button).
Expand Down Expand Up @@ -136,6 +151,14 @@ class NANOGUI_EXPORT Button : public Widget {
/// Whether or not this Button is currently pushed.
bool m_pushed;

/// Whether the button outline + background should be rendered
bool m_flat;

/// Horizontal padding on both sides
uint8_t m_horizontal_padding;
/// Vertical padding on both sides
uint8_t m_vertical_padding;

/// The current flags of this button (see \ref nanogui::Button::Flags for options).
int m_flags;

Expand Down
1 change: 1 addition & 0 deletions include/nanogui/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ enum class Cursor {
/* Forward declarations */
template <typename T> class ref;
class AdvancedGridLayout;
class BorderLayout;
class BoxLayout;
class Button;
class CheckBox;
Expand Down
7 changes: 6 additions & 1 deletion include/nanogui/popup.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ NAMESPACE_BEGIN(nanogui)
*/
class NANOGUI_EXPORT Popup : public Window {
public:
enum Side { Left = 0, Right };
enum Side { Left = 0, Right, LeftInside, RightInside };

/// Create a new popup parented to a screen (first argument) and a parent window (if applicable)
Popup(Widget *parent, Window *parent_window = nullptr);
Expand Down Expand Up @@ -62,6 +62,11 @@ class NANOGUI_EXPORT Popup : public Window {

/// Draw the popup window
virtual void draw(NVGcontext* ctx) override;

/// Recomputes the anchor position (given a reference widget)
void update_anchor(const Widget * ref);
/// Recomputes the anchor position (given a reference point)
void update_anchor(const Vector2i &p);
protected:
/// Internal helper function to maintain nested window position values
virtual void refresh_relative_placement() override;
Expand Down
3 changes: 3 additions & 0 deletions include/nanogui/widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ class NANOGUI_EXPORT Widget : public Object {
/// Remove a child widget by value
void remove_child(const Widget *widget);

/// Remove all children
void remove_all_children();

/// Retrieves the child at the specific position
const Widget* child_at(int index) const { return m_children[(size_t) index]; }

Expand Down
169 changes: 169 additions & 0 deletions src/borderlayout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
src/borderlayout.cpp -- Layout inspired by Java's BorderLayout

NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.

All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include <nanogui/borderlayout.h>
#include <nanogui/widget.h>
#include <nanogui/window.h>

NAMESPACE_BEGIN(nanogui)

static Vector2i get_size(NVGcontext *ctx, const Widget *w)
{
Vector2i ps = w->preferred_size(ctx), fs = w->fixed_size();
return Vector2i(
fs[0] ? fs[0] : ps[0],
fs[1] ? fs[1] : ps[1]
);
}

BorderLayout::BorderLayout(int margin, int spacing)
: m_margin(margin), m_spacing(spacing)
{
}

void BorderLayout::set_side(const Widget *widget, Side side)
{
m_side[widget] = side;
}

BorderLayout::Side BorderLayout::side(const Widget *widget) const
{
auto it = m_side.find(widget);
if (it == m_side.end())
return Center;
return it->second;
}

Vector2i BorderLayout::preferred_size(NVGcontext *ctx, const Widget *widget) const
{
Vector2i ret{ 0, 0 };

// First collect the center items
for (Widget *w : widget->children()) {
if (!w->visible())
continue;
Vector2i csize = get_size(ctx, w);
if (side(w) == Center) {
ret.x() = std::max(ret.x(), csize.x());
ret.y() = std::max(ret.y(), csize.y());
break;
}
}

// Then process the border items, but in reverse order!
for (int i = widget->child_count() - 1; i >= 0; i--) {
const Widget *w = widget->child_at(i);
if (!w->visible())
continue;
Vector2i csize = get_size(ctx, w);
switch (side(w)) {
case North:
case South:
ret.x() = std::max(ret.x(), csize.x());
ret.y() += csize.y() + m_spacing;
break;
case West:
case East:
ret.x() += csize.x() + m_spacing;
ret.y() = std::max(ret.y(), csize.y());
break;
case Center:
default:
/* NOP */
break;
}
}

// Add the margin
ret.x() += 2 * m_margin;
ret.y() += 2 * m_margin;

// Add the window title height
const Window *window = dynamic_cast<const Window *>(widget);
if (window && !window->title().empty()) {
ret.y() += widget->theme()->m_window_header_height;
}

return ret;
}

void BorderLayout::perform_layout(NVGcontext *ctx, Widget *widget) const
{
Vector2i pos(0, 0), size = widget->size();

// Add the window title height
const Window *window = dynamic_cast<const Window *>(widget);
if (window && !window->title().empty()) {
pos.y() += widget->theme()->m_window_header_height;
size.y() -= widget->theme()->m_window_header_height;
}

// Substract the margin
if (m_margin) {
pos += Vector2i{ m_margin, m_margin };
size -= Vector2i{ 2 * m_margin, 2 * m_margin };
}

// Loop through the widgets and handle the non-center widgets only
for (Widget *w : widget->children()) {
if (!w->visible())
continue;
Side s = side(w);
if (s == Center)
continue;
Vector2i csize = get_size(ctx, w);
switch (s) {
case North:
w->set_position(pos);
w->set_size({ size.x(), csize.y() });
pos.y() += csize.y() + m_spacing;
size.y() -= csize.y() + m_spacing;
break;
case South:
w->set_position({ pos.x(), pos.y() + size.y() - csize.y() });
w->set_size({ size.x(), csize.y() });
size.y() -= csize.y() + m_spacing;
break;
case West:
w->set_position(pos);
w->set_size({ csize.x(), size.y() });
pos.x() += csize.x() + m_spacing;
size.x() -= csize.x() + m_spacing;
break;
case East:
w->set_position({ pos.x() + size.x() - csize.x(), pos.y() });
w->set_size({ csize.x(), size.y() });
size.x() -= csize.x() + m_spacing;
break;
default:
/* NOP */
break;
}
}

// Loop through the center widgets and assign them the remaining space
for (Widget *w : widget->children()) {
if (!w->visible())
continue;
if (side(w) != Center)
continue;
w->set_position(pos);
w->set_size(size);
}

// Let the children perform layout as well
for (Widget *w : widget->children()) {
if (!w->visible())
continue;
w->perform_layout(ctx);
}
}

NAMESPACE_END(nanogui)
Loading