Skip to content

Commit

Permalink
Add a button group
Browse files Browse the repository at this point in the history
  • Loading branch information
darbyjohnston committed Apr 23, 2023
1 parent 7943b35 commit c25962d
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 104 deletions.
33 changes: 17 additions & 16 deletions examples/widgets-glfw/BasicWidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "BasicWidgets.h"

#include <tlUI/ButtonGroup.h>
#include <tlUI/GroupBox.h>
#include <tlUI/PushButton.h>
#include <tlUI/ToolButton.h>
Expand All @@ -17,8 +18,8 @@ namespace tl
{
struct BasicWidgets::Private
{
std::shared_ptr<ui::ButtonGroup> buttonGroup;
std::shared_ptr<ui::RowLayout> layout;
std::map<std::string, std::shared_ptr<observer::ValueObserver<bool> > > observers;
};

void BasicWidgets::_init(
Expand All @@ -29,40 +30,40 @@ namespace tl

auto pushButton0 = ui::PushButton::create(context);
pushButton0->setText("Click");
p.observers["pushButton0"] = observer::ValueObserver<bool>::create(
pushButton0->observeClick(),
[this](bool)
pushButton0->setClickedCallback(
[]()
{
std::cout << "Click" << std::endl;
},
observer::CallbackAction::Suppress);
});

auto pushButton1 = ui::PushButton::create(context);
pushButton1->setCheckable(true);
pushButton1->setChecked(true);
pushButton1->setText("Toggle");
pushButton1->setIcon("Settings");
p.observers["pushButton1"] = observer::ValueObserver<bool>::create(
pushButton1->observeChecked(),
[this](bool value)
pushButton1->setCheckedCallback(
[](bool value)
{
std::cout << "Toggle: " << value << std::endl;
},
observer::CallbackAction::Suppress);
});

p.buttonGroup = ui::ButtonGroup::create(ui::ButtonGroupType::Radio, context);
auto toolButton0 = ui::ToolButton::create(context);
toolButton0->setCheckable(true);
toolButton0->setChecked(true);
toolButton0->setIcon("PlaybackReverse");

p.buttonGroup->addButton(toolButton0);
auto toolButton1 = ui::ToolButton::create(context);
toolButton1->setCheckable(true);
toolButton1->setIcon("PlaybackStop");

p.buttonGroup->addButton(toolButton1);
auto toolButton2 = ui::ToolButton::create(context);
toolButton2->setCheckable(true);
toolButton2->setText("Forward");
toolButton2->setIcon("PlaybackForward");
p.buttonGroup->addButton(toolButton2);
p.buttonGroup->setCheckedCallback(
[](int index, bool value)
{
std::cout << "Radio: " << index << " " << value << std::endl;
});

p.layout = ui::VerticalLayout::create(context, shared_from_this());
auto groupBox = ui::GroupBox::create(context, p.layout);
Expand Down
89 changes: 30 additions & 59 deletions examples/widgets-glfw/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "NumericWidgets.h"
#include "RowLayouts.h"

#include <tlUI/ButtonGroup.h>
#include <tlUI/ListButton.h>
#include <tlUI/PushButton.h>
#include <tlUI/RowLayout.h>
Expand All @@ -24,13 +25,9 @@ namespace tl
{
struct MainWindow::Private
{
std::shared_ptr<ui::ButtonGroup> buttonGroup;
std::shared_ptr<ui::RowLayout> layout;
std::shared_ptr<ui::StackLayout> stackLayout;
std::shared_ptr<observer::ValueObserver<bool> > basicObserver;
std::shared_ptr<observer::ValueObserver<bool> > numericObserver;
std::shared_ptr<observer::ValueObserver<bool> > chartObserver;
std::shared_ptr<observer::ValueObserver<bool> > rowLayoutObserver;
std::shared_ptr<observer::ValueObserver<bool> > gridLayoutObserver;
};

void MainWindow::_init(
Expand All @@ -41,55 +38,30 @@ namespace tl

setBackgroundRole(ui::ColorRole::Window);

auto basicButton = ui::ListButton::create(context);
basicButton->setText("Basic Widgets");
p.basicObserver = observer::ValueObserver<bool>::create(
basicButton->observeClick(),
[this](bool)
std::vector<std::shared_ptr<ui::IButton> > buttons;
const std::vector<std::string> buttonText =
{
"Basic Widgets",
"Numeric Widgets",
"Charts",
"Row Layouts",
"Grid Layouts"
};
p.buttonGroup = ui::ButtonGroup::create(
ui::ButtonGroupType::Click,
context);
for (const auto& text : buttonText)
{
auto button = ui::ListButton::create(context);
button->setText(text);
buttons.push_back(button);
p.buttonGroup->addButton(button);
}
p.buttonGroup->setClickedCallback(
[this](int value)
{
_p->stackLayout->setCurrentIndex(0);
},
observer::CallbackAction::Suppress);

auto numericButton = ui::ListButton::create(context);
numericButton->setText("Numeric Widgets");
p.numericObserver = observer::ValueObserver<bool>::create(
numericButton->observeClick(),
[this](bool)
{
_p->stackLayout->setCurrentIndex(1);
},
observer::CallbackAction::Suppress);

auto chartButton = ui::ListButton::create(context);
chartButton->setText("Charts");
p.chartObserver = observer::ValueObserver<bool>::create(
chartButton->observeClick(),
[this](bool)
{
_p->stackLayout->setCurrentIndex(2);
},
observer::CallbackAction::Suppress);

auto rowLayoutButton = ui::ListButton::create(context);
rowLayoutButton->setText("Row Layouts");
p.rowLayoutObserver = observer::ValueObserver<bool>::create(
rowLayoutButton->observeClick(),
[this](bool)
{
_p->stackLayout->setCurrentIndex(3);
},
observer::CallbackAction::Suppress);

auto gridLayoutButton = ui::ListButton::create(context);
gridLayoutButton->setText("Grid Layouts");
p.gridLayoutObserver = observer::ValueObserver<bool>::create(
gridLayoutButton->observeClick(),
[this](bool)
{
_p->stackLayout->setCurrentIndex(4);
},
observer::CallbackAction::Suppress);
_p->stackLayout->setCurrentIndex(value);
});

auto basicWidgets = BasicWidgets::create(context);
auto numericWidgets = NumericWidgets::create(context);
Expand All @@ -106,11 +78,10 @@ namespace tl
p.layout);
auto buttonLayout = ui::VerticalLayout::create(context, scrollArea);
buttonLayout->setSpacingRole(ui::SizeRole::None);
basicButton->setParent(buttonLayout);
numericButton->setParent(buttonLayout);
chartButton->setParent(buttonLayout);
rowLayoutButton->setParent(buttonLayout);
gridLayoutButton->setParent(buttonLayout);
for (auto button : buttons)
{
button->setParent(buttonLayout);
}
p.stackLayout = ui::StackLayout::create(context, p.layout);
p.stackLayout->setHStretch(ui::Stretch::Expanding);
p.stackLayout->setVStretch(ui::Stretch::Expanding);
Expand All @@ -120,7 +91,7 @@ namespace tl
rowLayouts->setParent(p.stackLayout);
gridLayouts->setParent(p.stackLayout);

p.stackLayout->setCurrentIndex(2);
//p.stackLayout->setCurrentIndex(2);
}

MainWindow::MainWindow() :
Expand Down
118 changes: 118 additions & 0 deletions lib/tlUI/ButtonGroup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021-2023 Darby Johnston
// All rights reserved.

#include <tlUI/ButtonGroup.h>

namespace tl
{
namespace ui
{
struct ButtonGroup::Private
{
ButtonGroupType type = ButtonGroupType::Click;
std::vector<std::shared_ptr<IButton> > buttons;
int radio = -1;
std::function<void(int)> clickedCallback;
std::function<void(int, bool)> checkedCallback;
};

void ButtonGroup::_init(
ButtonGroupType type,
const std::shared_ptr<system::Context>& context)
{
TLRENDER_P();
p.type = type;
}

ButtonGroup::ButtonGroup() :
_p(new Private)
{}

ButtonGroup::~ButtonGroup()
{}

std::shared_ptr<ButtonGroup> ButtonGroup::create(
ButtonGroupType type,
const std::shared_ptr<system::Context>& context)
{
auto out = std::shared_ptr<ButtonGroup>(new ButtonGroup);
out->_init(type, context);
return out;
}

void ButtonGroup::addButton(const std::shared_ptr<IButton>& button)
{
TLRENDER_P();
switch (p.type)
{
case ButtonGroupType::Click:
button->setCheckable(false);
break;
case ButtonGroupType::Radio:
button->setCheckable(true);
break;
case ButtonGroupType::Toggle:
button->setCheckable(true);
break;
}
const size_t index = p.buttons.size();
p.buttons.push_back(button);
button->setClickedCallback(
[this, index]()
{
if (_p->clickedCallback)
{
_p->clickedCallback(index);
}
});
button->setCheckedCallback(
[this, index](bool value)
{
switch (_p->type)
{
case ButtonGroupType::Radio:
for (size_t i = 0; i < _p->buttons.size(); ++i)
{
_p->buttons[i]->setChecked(i == index);
}
if (_p->checkedCallback && index != _p->radio)
{
_p->checkedCallback(index, true);
}
_p->radio = index;
break;
case ButtonGroupType::Toggle:
for (size_t i = 0; i < _p->buttons.size(); ++i)
{
if (i != index)
{
_p->buttons[i]->setChecked(false);
}
}
if (_p->checkedCallback)
{
_p->checkedCallback(index, value);
}
break;
}
});
}

void ButtonGroup::clearButtons()
{
TLRENDER_P();
p.buttons.clear();
}

void ButtonGroup::setClickedCallback(const std::function<void(int)>& value)
{
_p->clickedCallback = value;
}

void ButtonGroup::setCheckedCallback(const std::function<void(int, bool)>& value)
{
_p->checkedCallback = value;
}
}
}
57 changes: 57 additions & 0 deletions lib/tlUI/ButtonGroup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021-2023 Darby Johnston
// All rights reserved.

#pragma once

#include <tlUI/IButton.h>

namespace tl
{
namespace ui
{
//! Button group type.
enum class ButtonGroupType
{
Click,
Radio,
Toggle
};

//! Button group.
class ButtonGroup : public std::enable_shared_from_this<ButtonGroup>
{
TLRENDER_NON_COPYABLE(ButtonGroup);

protected:
void _init(
ButtonGroupType,
const std::shared_ptr<system::Context>&);

ButtonGroup();

public:
~ButtonGroup();

//! Create a new button group.
static std::shared_ptr<ButtonGroup> create(
ButtonGroupType,
const std::shared_ptr<system::Context>&);

//! Add a button to the group.
void addButton(const std::shared_ptr<IButton>&);

//! Clear the buttons in the group.
void clearButtons();

//! Set the clicked callback.
void setClickedCallback(const std::function<void(int)>&);

//! Set the checked callback.
void setCheckedCallback(const std::function<void(int, bool)>&);

private:
TLRENDER_PRIVATE();
};
}
}
2 changes: 2 additions & 0 deletions lib/tlUI/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(HEADERS
ButtonGroup.h
DrawUtil.h
Event.h
EventLoop.h
Expand Down Expand Up @@ -38,6 +39,7 @@ set(HEADERS
set(HEADERS_PRIVATE)

set(SOURCE
ButtonGroup.cpp
DrawUtil.cpp
Event.cpp
EventLoop.cpp
Expand Down
Loading

0 comments on commit c25962d

Please sign in to comment.