Skip to content

Commit

Permalink
Add a splitter
Browse files Browse the repository at this point in the history
  • Loading branch information
darbyjohnston committed Apr 28, 2023
1 parent 5d6cb0b commit 7f3f508
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 4 deletions.
8 changes: 6 additions & 2 deletions examples/play-glfw/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <tlUI/ButtonGroup.h>
#include <tlUI/RowLayout.h>
#include <tlUI/ScrollArea.h>
#include <tlUI/Splitter.h>
#include <tlUI/TimelineViewport.h>
#include <tlUI/TimelineWidget.h>
#include <tlUI/ToolButton.h>
Expand All @@ -22,6 +23,7 @@ namespace tl
std::shared_ptr<ui::TimelineViewport> timelineViewport;
std::shared_ptr<ui::TimelineWidget> timelineWidget;
std::shared_ptr<ui::ButtonGroup> playbackButtonGroup;
std::shared_ptr<ui::Splitter> splitter;
std::shared_ptr<ui::RowLayout> layout;
std::shared_ptr<observer::ValueObserver<timeline::Playback> > playbackObserver;
};
Expand Down Expand Up @@ -54,8 +56,10 @@ namespace tl

p.layout = ui::VerticalLayout::create(context, shared_from_this());
p.layout->setSpacingRole(ui::SizeRole::None);
p.timelineViewport->setParent(p.layout);
p.timelineWidget->setParent(p.layout);
p.splitter = ui::Splitter::create(ui::Orientation::Vertical, context, p.layout);
p.splitter->setSplit(.7F);
p.timelineViewport->setParent(p.splitter);
p.timelineWidget->setParent(p.splitter);
auto hLayout = ui::HorizontalLayout::create(context, p.layout);
hLayout->setMarginRole(ui::SizeRole::MarginSmall);
hLayout->setSpacingRole(ui::SizeRole::SpacingTool);
Expand Down
2 changes: 2 additions & 0 deletions lib/tlUI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(HEADERS
RowLayout.h
ScrollArea.h
Spacer.h
Splitter.h
StackLayout.h
Style.h
TimelineAudioClipItem.h
Expand Down Expand Up @@ -67,6 +68,7 @@ set(SOURCE
RowLayout.cpp
ScrollArea.cpp
Spacer.cpp
Splitter.cpp
StackLayout.cpp
Style.cpp
TimelineAudioClipItem.cpp
Expand Down
244 changes: 244 additions & 0 deletions lib/tlUI/Splitter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021-2023 Darby Johnston
// All rights reserved.

#include <tlUI/Splitter.h>

#include <tlUI/GeometryUtil.h>

namespace tl
{
namespace ui
{
struct Splitter::Private
{
Orientation orientation = Orientation::Horizontal;
float split = .5F;

struct SizeData
{
int handle = 0;
std::vector<math::BBox2i> handleGeometry;
};
SizeData size;

struct MouseData
{
math::Vector2i pos;
int hoverHandle = -1;
int pressedHandle = -1;
};
MouseData mouse;
};

void Splitter::_init(
Orientation orientation,
const std::shared_ptr<system::Context>& context,
const std::shared_ptr<IWidget>& parent)
{
IWidget::_init("tl::ui::Splitter", context, parent);
TLRENDER_P();
_hStretch = Stretch::Expanding;
_vStretch = Stretch::Expanding;
p.orientation = orientation;
}

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

Splitter::~Splitter()
{}

std::shared_ptr<Splitter> Splitter::create(
Orientation orientation,
const std::shared_ptr<system::Context>& context,
const std::shared_ptr<IWidget>& parent)
{
auto out = std::shared_ptr<Splitter>(new Splitter);
out->_init(orientation, context, parent);
return out;
}

void Splitter::setSplit(float value)
{
TLRENDER_P();
if (value == p.split)
return;
p.split = value;
_updates |= Update::Size;
_updates |= Update::Draw;
}

void Splitter::setGeometry(const math::BBox2i& value)
{
IWidget::setGeometry(value);
TLRENDER_P();

const math::BBox2i g = _geometry;

p.size.handleGeometry.clear();
std::vector<math::BBox2i> childGeometry;
int x = g.x();
int y = g.y();
int w = 0;
int h = 0;
switch (p.orientation)
{
case Orientation::Horizontal:
w = g.w() * p.split - p.size.handle / 2;
h = g.h();
childGeometry.push_back(math::BBox2i(x, y, w, h));
x += w;
w = p.size.handle;
p.size.handleGeometry.push_back(math::BBox2i(x, y, w, h));
x += w;
w = g.w() - g.w() * p.split - p.size.handle;
childGeometry.push_back(math::BBox2i(x, y, w, h));
break;
case Orientation::Vertical:
w = g.w();
h = g.h() * p.split - p.size.handle / 2;
childGeometry.push_back(math::BBox2i(x, y, w, h));
y += h;
h = p.size.handle;
p.size.handleGeometry.push_back(math::BBox2i(x, y, w, h));
y += h;
h = g.h() - g.h() * p.split - p.size.handle;
childGeometry.push_back(math::BBox2i(x, y, w, h));
break;
}

size_t i = 0;
for (auto child : _children)
{
child->setGeometry(childGeometry[i]);
if (i < childGeometry.size() - 1)
{
++i;
}
}
}

void Splitter::sizeEvent(const SizeEvent& event)
{
IWidget::sizeEvent(event);
TLRENDER_P();

p.size.handle = event.style->getSizeRole(SizeRole::Handle);
const int sa = event.style->getSizeRole(SizeRole::ScrollArea);

_sizeHint.x = sa;
_sizeHint.y = sa;
}

void Splitter::drawEvent(const DrawEvent& event)
{
IWidget::drawEvent(event);
TLRENDER_P();

//event.render->drawRect(_geometry, imaging::Color4f(.5F, .3F, .3F));

for (const auto& handle : p.size.handleGeometry)
{
event.render->drawRect(
handle,
event.style->getColorRole(ColorRole::Button));
}
if (p.mouse.pressedHandle != -1)
{
event.render->drawRect(
p.size.handleGeometry[p.mouse.pressedHandle],
event.style->getColorRole(ColorRole::Pressed));
}
else if (p.mouse.hoverHandle != -1)
{
event.render->drawRect(
p.size.handleGeometry[p.mouse.hoverHandle],
event.style->getColorRole(ColorRole::Hover));
}
}

void Splitter::enterEvent()
{}

void Splitter::leaveEvent()
{
TLRENDER_P();
if (p.mouse.hoverHandle != -1)
{
p.mouse.hoverHandle = -1;
_updates |= Update::Draw;
}
}

void Splitter::mouseMoveEvent(MouseMoveEvent& event)
{
TLRENDER_P();
event.accept = true;
p.mouse.pos = event.pos;
if (p.mouse.pressedHandle != -1)
{
const math::BBox2i g = _geometry;
float split = 0.F;
switch (p.orientation)
{
case Orientation::Horizontal:
split = (p.mouse.pos.x - g.min.x) / static_cast<float>(g.w());
break;
case Orientation::Vertical:
split = (p.mouse.pos.y - g.min.y) / static_cast<float>(g.h());
break;
}
split = math::clamp(split, .1F, .9F);
if (split != p.split)
{
p.split = split;
_updates |= Update::Size;
_updates |= Update::Draw;
}
}
else
{
int hoverHandle = -1;
for (size_t i = 0; i < p.size.handleGeometry.size(); ++i)
{
if (p.size.handleGeometry[i].contains(p.mouse.pos))
{
hoverHandle = i;
break;
}
}
if (hoverHandle != p.mouse.hoverHandle)
{
p.mouse.hoverHandle = hoverHandle;
_updates |= Update::Draw;
}
}
}

void Splitter::mousePressEvent(MouseClickEvent& event)
{
TLRENDER_P();
event.accept = true;
p.mouse.pressedHandle = -1;
for (size_t i = 0; i < p.size.handleGeometry.size(); ++i)
{
if (p.size.handleGeometry[i].contains(p.mouse.pos))
{
p.mouse.pressedHandle = i;
_updates |= Update::Draw;
break;
}
}
}

void Splitter::mouseReleaseEvent(MouseClickEvent& event)
{
TLRENDER_P();
event.accept = true;
p.mouse.pressedHandle = -1;
_updates |= Update::Draw;
}
}
}
51 changes: 51 additions & 0 deletions lib/tlUI/Splitter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021-2023 Darby Johnston
// All rights reserved.

#pragma once

#include <tlUI/IWidget.h>

namespace tl
{
namespace ui
{
//! Splitter widget.
class Splitter : public IWidget
{
TLRENDER_NON_COPYABLE(Splitter);

protected:
void _init(
Orientation,
const std::shared_ptr<system::Context>&,
const std::shared_ptr<IWidget>& parent = nullptr);

Splitter();

public:
~Splitter() override;

//! Create a new splitter widget.
static std::shared_ptr<Splitter> create(
Orientation,
const std::shared_ptr<system::Context>&,
const std::shared_ptr<IWidget>& parent = nullptr);

//! Set the split amount.
void setSplit(float);

void setGeometry(const math::BBox2i&) override;
void sizeEvent(const SizeEvent&) override;
void drawEvent(const DrawEvent&) override;
void enterEvent() override;
void leaveEvent() override;
void mouseMoveEvent(MouseMoveEvent&) override;
void mousePressEvent(MouseClickEvent&) override;
void mouseReleaseEvent(MouseClickEvent&) override;

private:
TLRENDER_PRIVATE();
};
}
}
2 changes: 1 addition & 1 deletion lib/tlUI/Style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace tl
p.sizeRoles[SizeRole::SpacingTool] = 2;
p.sizeRoles[SizeRole::Border] = 1;
p.sizeRoles[SizeRole::ScrollArea] = 200;
p.sizeRoles[SizeRole::Handle] = 10;
p.sizeRoles[SizeRole::Handle] = 12;

p.colorRoles[ColorRole::Window] = imaging::Color4f(.2F, .2F, .2F);
p.colorRoles[ColorRole::Base] = imaging::Color4f(.17F, .17F, .17F);
Expand Down
2 changes: 1 addition & 1 deletion lib/tlUI/TimelineWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ namespace tl
IWidget::sizeEvent(event);
TLRENDER_P();

p.size.margin = event.style->getSizeRole(SizeRole::MarginSmall);
p.size.margin = event.style->getSizeRole(SizeRole::MarginSmall) * event.contentScale;

const int sa = event.style->getSizeRole(SizeRole::ScrollArea);
_sizeHint.x = sa;
Expand Down

0 comments on commit 7f3f508

Please sign in to comment.