Skip to content

Instantly share code, notes, and snippets.

@tyanmahou
Last active June 24, 2022 21:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyanmahou/a9fce1c3662fde8a5645756f9023e20c to your computer and use it in GitHub Desktop.
Save tyanmahou/a9fce1c3662fde8a5645756f9023e20c to your computer and use it in GitHub Desktop.
Siv3D VirtualWindow

Siv3D用 仮想ウィンドウクラス

vw::VirtualWindow window{};
window.draw([](const RectF& sceneScreen) {
  // something…
});
#include "Component.hpp"
#include "GrabCtrl.hpp"
#include "ScrollCtrl.hpp"
#include "WindowMover.hpp"
#include "WindowResizer.hpp"
namespace vw::detail
{
Component::Component(WindowParam& param) :
m_param(param),
m_grabCtrl(std::make_unique<GrabCtrl>(this)),
m_resizer(std::make_unique<WindowResizer>(this)),
m_scrollCtrl(std::make_unique<ScrollCtrl>(this)),
m_mover(std::make_unique<WindowMover>(this))
{
}
Component::~Component()
{
}
}
#pragma once
#include <memory>
namespace vw::detail
{
struct WindowParam;
class GrabCtrl;
class WindowResizer;
class ScrollCtrl;
class WindowMover;
/// <summary>
/// 機能統括
/// </summary>
class Component
{
public:
Component(WindowParam& param);
~Component();
WindowParam& param() const
{
return m_param;
}
GrabCtrl& grabCtrl() const
{
return *m_grabCtrl.get();
}
WindowResizer& resizer() const
{
return *m_resizer.get();
}
ScrollCtrl& scrollCtrl() const
{
return *m_scrollCtrl.get();
}
WindowMover& mover() const
{
return *m_mover.get();
}
private:
WindowParam& m_param;
std::unique_ptr<GrabCtrl> m_grabCtrl;
std::unique_ptr<WindowResizer> m_resizer;
std::unique_ptr<ScrollCtrl> m_scrollCtrl;
std::unique_ptr<WindowMover> m_mover;
};
}
#pragma once
#include <Siv3D/Vector2D.hpp>
namespace vw::detail
{
/// <summary>
/// 定数
/// </summary>
class Constants
{
public:
static constexpr double Margin = 5.0;
static constexpr double ScrollMargin = 15.0;
static constexpr double ScrollBarSize = 20.0;
static constexpr s3d::Vec2 WinMinSize{
ScrollMargin * 3.0 + Margin + ScrollBarSize,
ScrollMargin * 3.0 + Margin + ScrollBarSize
};
};
}
#include "GrabCtrl.hpp"
#include "Component.hpp"
#include "WindowParam.hpp"
#include <Siv3D.hpp>
namespace
{
using namespace vw::detail;
s3d::Optional<s3d::CursorStyle> GetCursorStyle(GrabState grab)
{
switch (grab) {
case GrabState::Tl:
return CursorStyle::ResizeNWSE;
case GrabState::Tr:
return CursorStyle::ResizeNESW;
case GrabState::Bl:
return CursorStyle::ResizeNESW;
case GrabState::Br:
return CursorStyle::ResizeNWSE;
case GrabState::Top:
return CursorStyle::ResizeUpDown;
case GrabState::Bottom:
return CursorStyle::ResizeUpDown;
case GrabState::Left:
return CursorStyle::ResizeLeftRight;
case GrabState::Right:
return CursorStyle::ResizeLeftRight;
case GrabState::Move:
return CursorStyle::Default;
default:
break;
}
return s3d::none;
}
}
namespace vw::detail
{
GrabCtrl::GrabCtrl(Component* pComp) :
m_pComp(pComp)
{
}
void GrabCtrl::update()
{
if (!m_isGrab) {
bool isPromised = false;
for (auto* handler : m_handlers) {
if (handler->onGrabPromise()) {
isPromised = true;
m_promosedHandler = handler;
break;
}
}
for (auto* handler : m_handlers) {
handler->onPreGrabCheck(m_promosedHandler == handler);
}
if (isPromised) {
if (auto grabState = m_promosedHandler->onGrabCheck()) {
m_isGrab = true;
m_grabState = *grabState;
m_grabPos = m_pComp->param().pos;
m_grabSize = m_pComp->param().size;
m_grabScenePos = m_pComp->param().scenePos;
m_grabCursorPos = s3d::Cursor::PosF();
m_promosedHandler->onGrab(m_grabState);
}
}
} else if (!MouseL.pressed()) {
m_isGrab = false;
m_promosedHandler->onGrabRelease();
m_promosedHandler = nullptr;
}
if (m_isGrab) {
::GetCursorStyle(m_grabState).then([](CursorStyle stlye) {
s3d::Cursor::RequestStyle(stlye);
});
m_promosedHandler->onGrabUpdate(m_grabState);
}
}
void GrabCtrl::registHandler(IGrabHandler* handler)
{
m_handlers << handler;
}
}
#pragma once
#include <Siv3D/Array.hpp>
#include <Siv3D/CursorStyle.hpp>
#include <Siv3D/Optional.hpp>
#include <Siv3D/Vector2D.hpp>
namespace vw::detail
{
class Component;
enum class GrabState
{
None,
Top, Bottom, Left, Right,
Tl, Tr, Bl, Br,
Move,
ScrollV, ScrollH,
ScrollUp, ScrollDown, ScrollLeft, ScrollRight,
ScrollVTo, ScrollHTo,
};
class IGrabHandler
{
public:
virtual ~IGrabHandler() = default;
/// <summary>
/// 掴めるかどうか
/// </summary>
/// <returns>掴めるなら true で以降の他のハンドラはスルーされる</returns>
virtual bool onGrabPromise() = 0;
/// <summary>
/// 掴みチェックの前の更新処理
/// 全てのハンドラに呼ばれる
/// </summary>
/// <param name="isPromised"></param>
virtual void onPreGrabCheck(bool isPromised) = 0;
/// <summary>
/// 掴みチェック
/// </summary>
/// <returns></returns>
virtual s3d::Optional<GrabState> onGrabCheck() = 0;
/// <summary>
/// 捕まれた
/// </summary>
/// <param name="state"></param>
virtual void onGrab(GrabState state) = 0;
/// <summary>
/// 捕まれ中
/// </summary>
/// <param name="state"></param>
virtual void onGrabUpdate(GrabState state) = 0;
/// <summary>
/// 離された
/// </summary>
virtual void onGrabRelease() = 0;
};
/// <summary>
/// 掴み制御
/// </summary>
class GrabCtrl
{
public:
GrabCtrl(Component* pComp);
bool isGrab() const { return m_isGrab; }
const s3d::Vec2& grabCursorPos() const
{
return m_grabCursorPos;
}
const s3d::Vec2& grabPos() const
{
return m_grabPos;
}
const s3d::Vec2& grabSize() const
{
return m_grabSize;
}
const s3d::Vec2& grabScenePos() const
{
return m_grabScenePos;
}
void update();
void registHandler(IGrabHandler* handler);
private:
Component* m_pComp;
bool m_isGrab = false;
GrabState m_grabState{};
s3d::Vec2 m_grabCursorPos{};
s3d::Vec2 m_grabPos{};
s3d::Vec2 m_grabSize{};
s3d::Vec2 m_grabScenePos{};
s3d::Array<IGrabHandler*> m_handlers;
IGrabHandler* m_promosedHandler = nullptr;
};
}
#include "ScrollCtrl.hpp"
#include "Constants.hpp"
#include "Component.hpp"
#include "WindowParam.hpp"
#include <Siv3D.hpp>
namespace vw::detail
{
class ScrollCtrl::View
{
public:
View(const WindowParam& param) :
m_param(param)
{}
s3d::RectF barV() const
{
double minusH = this->hasScrollH() ? Constants::ScrollMargin : 0;
return {
m_param.region.x + m_param.region.w - Constants::ScrollMargin, m_param.region.y,
Constants::ScrollMargin, m_param.size.y - minusH
};
}
s3d::RectF gripV(bool isFixMargin = true) const
{
auto bar = this->barV();
auto [hight, offsetMax] = this->gripVHightAndOffsetMax();
auto yOffs = s3d::Math::Lerp(
0,
offsetMax,
s3d::Saturate(m_param.scenePos.y / (m_param.sceneSize.y - m_param.size.y))
);
double pushMargin = 1.0;
if (!isFixMargin) {
pushMargin = m_onTimeV;
}
return {
bar.x + (5.0 - pushMargin),
bar.y + bar.w + yOffs,
bar.w - (5.0 - pushMargin) * 2.0,
hight
};
}
RectF upBtn() const
{
RectF bar = this->barV();
return { bar.pos, bar.w, bar.w };
}
RectF downBtn() const
{
RectF bar = this->barV();
const Vec2 basePos{ bar.x, bar.y + bar.h - Constants::ScrollMargin };
return { basePos, bar.w, bar.w };
}
bool hasScrollV() const
{
return m_param.sceneSize.y > m_param.size.y;
}
bool isBarVClicked() const
{
if (!this->hasScrollV()) {
return false;
}
return this->barV().leftClicked();
}
bool isBarVMouseOver() const
{
if (!this->hasScrollV()) {
return false;
}
return this->barV().mouseOver();
}
bool isGripVClicked() const
{
if (!this->hasScrollV()) {
return false;
}
return this->gripV().leftClicked();
}
std::pair<double, double> gripVHightAndOffsetMax() const
{
auto bar = this->barV();
auto hight = s3d::Math::Lerp(
Constants::ScrollBarSize,
bar.h - bar.w * 2.0,
s3d::Saturate(m_param.size.y / m_param.sceneSize.y)
);
return { hight, bar.h - bar.w * 2.0 - hight };
}
double toSceneYFromGripDiff(const s3d::Vec2& scenePos, double diff) const
{
const double moveable = (m_param.sceneSize.y - m_param.region.size.y);
if (moveable <= 0) {
return 0;
}
auto [hight, offsetMax] = this->gripVHightAndOffsetMax();
return s3d::Clamp(scenePos.y + diff * moveable / offsetMax, 0.0, moveable);
}
bool isUpBtnMouseOver() const
{
if (!hasScrollV()) {
return false;
}
return this->upBtn().mouseOver();
}
bool isUpBtnClicked() const
{
if (!hasScrollV()) {
return false;
}
return this->upBtn().leftClicked();
}
bool isDownBtnMouseOver() const
{
if (!hasScrollV()) {
return false;
}
return this->downBtn().mouseOver();
}
bool isDownBtnClicked() const
{
if (!hasScrollV()) {
return false;
}
return this->downBtn().leftClicked();
}
void drawV(const ColorF& barColor, const ColorF& scrollColor) const
{
// スクロールバー
RectF bar = this->barV();
bar.draw(barColor);
{
const double pushMargin = m_pushUpBtn ? 0.0 : 1.0;
Triangle(
{ bar.x + bar.w * 0.5, bar.y + (5.0 - pushMargin) },
{ bar.x + (4.0 - pushMargin), bar.y + bar.w - (5.0 - pushMargin) },
{ bar.x + bar.w - (4.0 - pushMargin) , bar.y + bar.w - (5.0 - pushMargin) }
).draw(ColorF(scrollColor, m_onTimeV));
}
{
const Vec2 basePos{ bar.x, bar.y + bar.h - Constants::ScrollMargin };
const double pushMargin = m_pushDownBtn ? 0.0 : 1.0;
Triangle(
{ bar.x + bar.w * 0.5, bar.y + bar.h - (5.0 - pushMargin) },
{ bar.x + (4.0 - pushMargin), bar.y + bar.h - (bar.w - (5.0 - pushMargin)) },
{ bar.x + bar.w - (4.0 - pushMargin), bar.y + bar.h - (bar.w - (5.0 - pushMargin)) }
).draw(ColorF(scrollColor, m_onTimeV));
}
{
RoundRect(this->gripV(false), 2.0).draw(scrollColor);
}
}
s3d::RectF barH() const
{
double minusW = this->hasScrollV() ? Constants::ScrollMargin : 0;
return {
m_param.region.x, m_param.region.y + m_param.region.h - Constants::ScrollMargin,
m_param.size.x - minusW, Constants::ScrollMargin
};
}
s3d::RectF gripH(bool isFixMargin = true) const
{
auto bar = this->barH();
auto [width, offsetMax] = this->gripHWidthAndOffsetMax();
auto xOffs = s3d::Math::Lerp(
0,
offsetMax,
s3d::Saturate(m_param.scenePos.x / (m_param.sceneSize.x - m_param.size.x))
);
double pushMargin = 1.0;
if (!isFixMargin) {
pushMargin = m_onTimeH;
}
return{
bar.x + bar.h + xOffs,
bar.y + (5.0 - pushMargin),
width,
bar.h - (5.0 - pushMargin) * 2.0
};
}
RectF leftBtn() const
{
RectF bar = this->barH();
return { bar.pos, bar.h, bar.h };
}
RectF rightBtn() const
{
RectF bar = this->barH();
const Vec2 basePos{ bar.x + bar.w - Constants::ScrollMargin, bar.y };
return { basePos, bar.h, bar.h };
}
bool hasScrollH() const
{
return m_param.sceneSize.x > m_param.size.x;
}
bool isBarHClicked() const
{
if (!this->hasScrollH()) {
return false;
}
return this->barH().leftClicked();
}
bool isBarHMouseOver() const
{
if (!this->hasScrollH()) {
return false;
}
return this->barH().mouseOver();
}
bool isGripHClicked() const
{
if (!this->hasScrollH()) {
return false;
}
return this->gripH().leftClicked();
}
std::pair<double, double> gripHWidthAndOffsetMax() const
{
auto bar = this->barH();
auto width = s3d::Math::Lerp(
Constants::ScrollBarSize,
bar.w - bar.h * 2.0,
s3d::Saturate(m_param.size.x / m_param.sceneSize.x)
);
return { width, bar.w - bar.h * 2.0 - width };
}
double toSceneXFromGripDiff(const s3d::Vec2& scenePos, double diff) const
{
const double moveable = (m_param.sceneSize.x - m_param.size.x);
if (moveable <= 0) {
return 0;
}
auto [hight, offsetMax] = this->gripHWidthAndOffsetMax();
return s3d::Clamp(scenePos.x + diff * moveable / offsetMax, 0.0, moveable);
}
bool isLeftBtnMouseOver() const
{
if (!hasScrollH()) {
return false;
}
return this->leftBtn().mouseOver();
}
bool isLeftBtnClicked() const
{
if (!hasScrollH()) {
return false;
}
return this->leftBtn().leftClicked();
}
bool isRightBtnMouseOver() const
{
if (!hasScrollH()) {
return false;
}
return this->rightBtn().mouseOver();
}
bool isRightBtnClicked() const
{
if (!hasScrollH()) {
return false;
}
return this->rightBtn().leftClicked();
}
void drawH(const ColorF& barColor, const ColorF& gripColor) const
{
// スクロールバー
RectF bar = this->barH();
bar.draw(barColor);
{
const double pushMargin = m_pushLeftBtn ? 0.0 : 1.0;
Triangle(
{ bar.x + (5.0 - pushMargin), bar.y + bar.h * 0.5 },
{ bar.x + bar.h - (5.0 - pushMargin), bar.y + (4.0 - pushMargin) },
{ bar.x + bar.h - (5.0 - pushMargin) , bar.y + bar.h - (4.0 - pushMargin) }
).draw(ColorF(gripColor, m_onTimeH));
}
{
const Vec2 basePos{ bar.x + bar.w - Constants::ScrollMargin, bar.y };
const double pushMargin = m_pushRightBtn ? 0.0 : 1.0;
Triangle(
{ bar.x + bar.w - (5.0 - pushMargin), bar.y + bar.h * 0.5 },
{ bar.x + bar.w - (bar.h - (5.0 - pushMargin)) , bar.y + (4.0 - pushMargin) },
{ bar.x + bar.w - (bar.h - (5.0 - pushMargin)) , bar.y + bar.h - (4.0 - pushMargin) }
).draw(ColorF(gripColor, m_onTimeH));
}
{
RoundRect(this->gripH(false), 2.0).draw(gripColor);
}
}
void draw(const ColorF& barColor, const ColorF& gripColor) const
{
const Vec2& size = m_param.size;
bool hasScrollV = m_param.sceneSize.y > size.y;
bool hasScrollH = m_param.sceneSize.x > size.x;
if (hasScrollV) {
this->drawV(barColor, gripColor);
}
if (hasScrollH) {
this->drawH(barColor, gripColor);
}
if (hasScrollH && hasScrollV) {
RectF(
m_param.region.x + m_param.region.w - Constants::ScrollMargin,
m_param.region.y + m_param.region.h - Constants::ScrollMargin,
Constants::ScrollMargin,
Constants::ScrollMargin
).draw(barColor);
}
}
void updateOnTime(bool isBarVMouseOver, bool isBarHMouseOver, double dt)
{
if (isBarVMouseOver) {
m_onTimeV = s3d::Saturate(m_onTimeV + dt * 5.0);
} else {
m_onTimeV = s3d::Saturate(m_onTimeV - dt * 2.0);
}
if (isBarHMouseOver) {
m_onTimeH = s3d::Saturate(m_onTimeH + dt * 5.0);
} else {
m_onTimeH = s3d::Saturate(m_onTimeH - dt * 2.0);
}
}
void pushUpBtn()
{
m_pushUpBtn = true;
}
void pushDownBtn()
{
m_pushDownBtn = true;
}
void pushLeftBtn()
{
m_pushLeftBtn = true;
}
void pushRightBtn()
{
m_pushRightBtn = true;
}
void resetPush()
{
m_pushUpBtn = false;
m_pushDownBtn = false;
m_pushLeftBtn = false;
m_pushRightBtn = false;
}
private:
const WindowParam& m_param;
double m_onTimeV = 0;
double m_onTimeH = 0;
bool m_pushUpBtn = false;
bool m_pushDownBtn = false;
bool m_pushLeftBtn = false;
bool m_pushRightBtn = false;
};
ScrollCtrl::ScrollCtrl(Component* pComp) :
m_pComp(pComp),
m_view(std::make_unique<View>(pComp->param()))
{
grab().registHandler(this);
}
void ScrollCtrl::update()
{
if (!m_pComp->param().canScroll) {
return;
}
if (grab().isGrab()) {
return;
}
if (m_pComp->param().region.mouseOver()) {
constexpr double speed = 30.0;
if (KeyShift.pressed()) {
m_pComp->param().scenePos.x += Mouse::Wheel() * speed;
} else {
m_pComp->param().scenePos.y += Mouse::Wheel() * speed;
m_pComp->param().scenePos.x += Mouse::WheelH() * speed;
}
}
}
void ScrollCtrl::draw(const ColorF& barColor, const ColorF& gripColor) const
{
m_view->draw(barColor, gripColor);
}
bool ScrollCtrl::onGrabPromise()
{
if (!m_pComp->param().canScroll) {
return false;
}
if (m_view->isBarVMouseOver()) {
return true;
} else if (m_view->isBarHMouseOver()) {
return true;
}
return false;
}
void ScrollCtrl::onPreGrabCheck(bool isPromised)
{
bool isBarVMouseOver = false;
bool isBarHMouseOver = false;
if (isPromised) {
if (m_view->isBarVMouseOver()) {
isBarVMouseOver = true;
} else if (m_view->isBarHMouseOver()) {
isBarHMouseOver = true;
}
}
const double dt = Scene::DeltaTime();
// スクロールバーの演出
m_view->updateOnTime(isBarVMouseOver, isBarHMouseOver, dt);
}
s3d::Optional<GrabState> ScrollCtrl::onGrabCheck()
{
if (m_view->isUpBtnClicked()) {
return GrabState::ScrollUp;
} else if (m_view->isDownBtnClicked()) {
return GrabState::ScrollDown;
} else if (m_view->isGripVClicked()) {
return GrabState::ScrollV;
} else if (m_view->isBarVClicked()) {
return GrabState::ScrollVTo;
} else if (m_view->isLeftBtnClicked()) {
return GrabState::ScrollLeft;
} else if (m_view->isRightBtnClicked()) {
return GrabState::ScrollRight;
} else if (m_view->isGripHClicked()) {
return GrabState::ScrollH;
} else if (m_view->isBarHClicked()) {
return GrabState::ScrollHTo;
}
return s3d::none;
}
void ScrollCtrl::onGrab(GrabState state)
{
m_waitTimer = 0;
auto& param = m_pComp->param();
const auto& grabCursorPos = grab().grabCursorPos();
const auto& grabScenePos = grab().grabScenePos();
if (state == GrabState::ScrollUp) {
m_view->pushUpBtn();
param.scenePos.y -= 1;
} else if (state == GrabState::ScrollDown) {
m_view->pushDownBtn();
param.scenePos.y += 1;
} else if (state == GrabState::ScrollLeft) {
m_view->pushLeftBtn();
param.scenePos.x -= 1;
} else if (state == GrabState::ScrollRight) {
m_view->pushRightBtn();
param.scenePos.x += 1;
} else if (state == GrabState::ScrollVTo) {
auto moveDiff = m_view->gripVHightAndOffsetMax().first;
const auto gripV = m_view->gripV();
if (gripV.y > grabCursorPos.y) {
moveDiff *= -1.0;
} else if (gripV.y + gripV.h < grabCursorPos.y) {
moveDiff *= 1.0;
} else {
moveDiff *= 0.0;
}
param.scenePos.y = m_view->toSceneYFromGripDiff(grabScenePos, moveDiff);
} else if (state == GrabState::ScrollHTo) {
auto moveDiff = m_view->gripHWidthAndOffsetMax().first;
const auto gripH = m_view->gripH();
if (gripH.x > grabCursorPos.x) {
moveDiff *= -1.0;
} else if (gripH.x + gripH.w < grabCursorPos.x) {
moveDiff *= 1.0;
} else {
moveDiff *= 0.0;
}
param.scenePos.x = m_view->toSceneXFromGripDiff(grabScenePos, moveDiff);
}
}
void ScrollCtrl::onGrabUpdate(GrabState state)
{
auto dt = Scene::DeltaTime();
m_waitTimer += dt;
auto& param = m_pComp->param();
const auto& grabCursorPos = grab().grabCursorPos();
const auto& grabScenePos = grab().grabScenePos();
auto delta = Cursor::PosF() - grabCursorPos;
if (state == GrabState::ScrollV) {
// スクロール
param.scenePos.y = m_view->toSceneYFromGripDiff(grabScenePos, delta.y);
} else if (state == GrabState::ScrollH) {
// スクロール
param.scenePos.x = m_view->toSceneXFromGripDiff(grabScenePos, delta.x);
} else if (state == GrabState::ScrollUp) {
if (m_waitTimer >= 0.5) {
if (m_view->isUpBtnMouseOver()) {
param.scenePos.y -= 200.0 * dt;
}
}
} else if (state == GrabState::ScrollDown) {
if (m_waitTimer >= 0.5) {
if (m_view->isDownBtnMouseOver()) {
param.scenePos.y += 200.0 * dt;
}
}
} else if (state == GrabState::ScrollLeft) {
if (m_waitTimer >= 0.5) {
if (m_view->isLeftBtnMouseOver()) {
param.scenePos.x -= 200.0 * dt;
}
}
} else if (state == GrabState::ScrollRight) {
if (m_waitTimer >= 0.5) {
if (m_view->isRightBtnMouseOver()) {
param.scenePos.x += 200.0 * dt;
}
}
} else if (state == GrabState::ScrollVTo) {
if (m_waitTimer >= 0.5) {
if (m_view->isBarVMouseOver()) {
auto moveDiff = m_view->gripVHightAndOffsetMax().first * 60.0 * dt;
const auto gripV = m_view->gripV();
if (gripV.y > grabCursorPos.y) {
moveDiff *= -1.0;
} else if (gripV.y + gripV.h < grabCursorPos.y) {
moveDiff *= 1.0;
} else {
moveDiff *= 0.0;
}
param.scenePos.y = m_view->toSceneYFromGripDiff(param.scenePos, moveDiff);
}
}
} else if (state == GrabState::ScrollHTo) {
if (m_waitTimer >= 0.5) {
if (m_view->isBarHMouseOver()) {
auto moveDiff = m_view->gripHWidthAndOffsetMax().first * 60.0 * dt;
const auto gripH = m_view->gripH();
if (gripH.x > grabCursorPos.x) {
moveDiff *= -1.0;
} else if (gripH.x + gripH.w < grabCursorPos.x) {
moveDiff *= 1.0;
} else {
moveDiff *= 0.0;
}
param.scenePos.x = m_view->toSceneXFromGripDiff(param.scenePos, moveDiff);
}
}
}
}
void ScrollCtrl::onGrabRelease()
{
m_view->resetPush();
}
GrabCtrl& ScrollCtrl::grab() const
{
return m_pComp->grabCtrl();
}
}
#pragma once
#include <memory>
#include "GrabCtrl.hpp"
namespace vw::detail
{
class Component;
/// <summary>
/// シーンスクロール制御
/// </summary>
class ScrollCtrl : public IGrabHandler
{
public:
class View;
public:
ScrollCtrl(Component* pComp);
void update();
void draw(const s3d::ColorF& barColor, const s3d::ColorF& gripColor) const;
public:
bool onGrabPromise() override;
void onPreGrabCheck(bool isPromised) override;
s3d::Optional<GrabState> onGrabCheck() override;
void onGrab(GrabState state);
void onGrabUpdate(GrabState state);
void onGrabRelease();
private:
GrabCtrl& grab() const;
private:
Component* m_pComp;
std::unique_ptr<View> m_view;
double m_waitTimer = 0;
};
}
#include "WindowMover.hpp"
#include "Constants.hpp"
#include "Component.hpp"
#include "WindowParam.hpp"
namespace vw::detail
{
WindowMover::WindowMover(Component* pComp) :
m_pComp(pComp)
{
grab().registHandler(this);
}
bool WindowMover::onGrabPromise()
{
auto& param = m_pComp->param();
if (!param.canMove) {
return false;
}
const auto rect = param.region;
return rect.mouseOver();
}
void WindowMover::onPreGrabCheck([[maybe_unused]] bool isPromised)
{
}
s3d::Optional<GrabState> WindowMover::onGrabCheck()
{
auto& param = m_pComp->param();
const auto rect = param.region;
if (rect.leftClicked()) {
return GrabState::Move;
}
return s3d::none;
}
void WindowMover::onGrab([[maybe_unused]] GrabState state)
{
}
void WindowMover::onGrabUpdate(GrabState state)
{
const auto& grabPos = grab().grabPos();
auto& param = m_pComp->param();
auto delta = Cursor::PosF() - grab().grabCursorPos();
if (state == GrabState::Move) {
// 移動
param.pos = grabPos + delta;
}
}
void WindowMover::onGrabRelease()
{
}
GrabCtrl& WindowMover::grab() const
{
return m_pComp->grabCtrl();
}
}
#pragma once
#include "GrabCtrl.hpp"
namespace vw::detail
{
class Component;
/// <summary>
/// ウィンドウ移動制御
/// </summary>
class WindowMover : public IGrabHandler
{
public:
WindowMover(Component* pComp);
public:
bool onGrabPromise() override;
void onPreGrabCheck(bool isPromised) override;
s3d::Optional<GrabState> onGrabCheck() override;
void onGrab(GrabState state);
void onGrabUpdate(GrabState state);
void onGrabRelease();
private:
GrabCtrl& grab() const;
private:
Component* m_pComp;
};
}
#pragma once
#include <Siv3D/RectF.hpp>
namespace vw::detail
{
struct WindowParam
{
SIV3D_DISABLE_MSVC_WARNINGS_PUSH(4201)
union {
struct {
s3d::Vec2 pos;
s3d::Vec2 size;
};
struct {
s3d::RectF region;
};
};
SIV3D_DISABLE_MSVC_WARNINGS_POP()
s3d::Vec2 scenePos;
s3d::Vec2 sceneSize;
s3d::Optional<ColorF> backGroundColor;
s3d::Optional<ColorF> frameColor;
s3d::ColorF scrollBarColor;
s3d::ColorF scrollGripColor;
bool canResize;
bool isResizeClampSceneSize;
bool canMove;
bool canScroll;
};
}
#include "WindowResizer.hpp"
#include "Constants.hpp"
#include "Component.hpp"
#include "WindowParam.hpp"
#include <Siv3D.hpp>
namespace vw::detail
{
WindowResizer::WindowResizer(Component* pComp) :
m_pComp(pComp)
{
grab().registHandler(this);
}
bool WindowResizer::onGrabPromise()
{
auto& param = m_pComp->param();
if (!param.canResize) {
return false;
}
constexpr auto margin = Constants::Margin;
const auto rect = param.region;
const RectF tl{ rect.x - margin, rect.y - margin, margin, margin };
const RectF tr{ rect.x + rect.w, rect.y - margin, margin, margin };
const RectF bl{ rect.x - margin, rect.y + rect.h, margin, margin };
const RectF br{ rect.x + rect.w, rect.y + rect.h, margin, margin };
const RectF top{ rect.x, rect.y - margin , rect.w, margin };
const RectF bottom{ rect.x, rect.y + rect.h , rect.w, margin };
const RectF left{ rect.x - margin, rect.y, margin, rect.h };
const RectF right{ rect.x + rect.w, rect.y, margin, rect.h };
if (param.canMove && tl.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeNWSE);
} else if (param.canMove && tr.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeNESW);
} else if (param.canMove && bl.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeNESW);
} else if (br.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeNWSE);
} else if ((param.canMove && top.mouseOver()) || bottom.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeUpDown);
} else if ((param.canMove && left.mouseOver()) || right.mouseOver()) {
s3d::Cursor::RequestStyle(CursorStyle::ResizeLeftRight);
} else {
return false;
}
return true;
}
void WindowResizer::onPreGrabCheck([[maybe_unused]] bool isPromised)
{
}
s3d::Optional<GrabState> WindowResizer::onGrabCheck()
{
constexpr auto margin = Constants::Margin;
auto& param = m_pComp->param();
const auto rect = param.region;
const RectF tl{ rect.x - margin, rect.y - margin, margin, margin };
const RectF tr{ rect.x + rect.w, rect.y - margin, margin, margin };
const RectF bl{ rect.x - margin, rect.y + rect.h, margin, margin };
const RectF br{ rect.x + rect.w, rect.y + rect.h, margin, margin };
const RectF top{ rect.x, rect.y - margin , rect.w, margin };
const RectF bottom{ rect.x, rect.y + rect.h , rect.w, margin };
const RectF left{ rect.x - margin, rect.y, margin, rect.h };
const RectF right{ rect.x + rect.w, rect.y, margin, rect.h };
if (param.canMove && tl.leftClicked()) {
return GrabState::Tl;
} else if (param.canMove && tr.leftClicked()) {
return GrabState::Tr;
} else if (param.canMove && bl.leftClicked()) {
return GrabState::Bl;
} else if (br.leftClicked()) {
return GrabState::Br;
} else if (param.canMove && top.leftClicked()) {
return GrabState::Top;
} else if (bottom.leftClicked()) {
return GrabState::Bottom;
} else if (param.canMove && left.leftClicked()) {
return GrabState::Left;
} else if (right.leftClicked()) {
return GrabState::Right;
}
return s3d::none;
}
void WindowResizer::onGrab([[maybe_unused]] GrabState state)
{
}
void WindowResizer::onGrabUpdate(GrabState state)
{
constexpr auto minSize = Constants::WinMinSize;
auto& param = m_pComp->param();
const auto& grabPos = grab().grabPos();
const auto& grabSize = grab().grabSize();
auto delta = Cursor::PosF() - grab().grabCursorPos();
if (state == GrabState::Top || state == GrabState::Tl || state == GrabState::Tr) {
// 上
if (grabSize.y - delta.y < minSize.y) {
delta.y = -(minSize.y - grabSize.y);
}
param.pos.y = grabPos.y + delta.y;
param.size.y = grabSize.y - delta.y;
} else if (state == GrabState::Bottom || state == GrabState::Bl || state == GrabState::Br) {
// 下
if (grabSize.y + delta.y < minSize.y) {
delta.y = minSize.y - grabSize.y;
}
param.size.y = grabSize.y + delta.y;
}
if (state == GrabState::Left || state == GrabState::Tl || state == GrabState::Bl) {
// 左
if (grabSize.x - delta.x < minSize.x) {
delta.x = -(minSize.x - grabSize.x);
}
param.pos.x = grabPos.x + delta.x;
param.size.x = grabSize.x - delta.x;
} else if (state == GrabState::Right || state == GrabState::Tr || state == GrabState::Br) {
// 右
if (grabSize.x + delta.x < minSize.x) {
delta.x = minSize.x - grabSize.x;
}
param.size.x = grabSize.x + delta.x;
}
}
void WindowResizer::onGrabRelease()
{
}
GrabCtrl& WindowResizer::grab() const
{
return m_pComp->grabCtrl();
}
}
#pragma once
#include "GrabCtrl.hpp"
namespace vw::detail
{
class Component;
/// <summary>
/// ウィンドウサイズ制御
/// </summary>
class WindowResizer : public IGrabHandler
{
public:
WindowResizer(Component* pComp);
public:
bool onGrabPromise() override;
void onPreGrabCheck(bool isPromised) override;
s3d::Optional<GrabState> onGrabCheck() override;
void onGrab(GrabState state);
void onGrabUpdate(GrabState state);
void onGrabRelease();
private:
GrabCtrl& grab() const;
private:
Component* m_pComp;
};
}
# include <Siv3D.hpp> // OpenSiv3D v0.6.4
# include "VirtualWindow.hpp"
void Main()
{
// 通常のフォントを作成 | Create a new font
const Font font{ 60 };
// 絵文字用フォントを作成 | Create a new emoji font
const Font emojiFont{ 60, Typeface::ColorEmoji };
// `font` が絵文字用フォントも使えるようにする | Set emojiFont as a fallback
font.addFallback(emojiFont);
// 画像ファイルからテクスチャを作成 | Create a texture from an image file
const Texture texture{ U"example/windmill.png" };
// 絵文字からテクスチャを作成 | Create a texture from an emoji
const Texture emoji{ U"🐈"_emoji };
// 絵文字を描画する座標 | Coordinates of the emoji
Vec2 emojiPos{ 300, 150 };
const Vec2 pos{ 0, 0 };
const Vec2 windowSize{ 300, 300 };
const Vec2 sceneSize = Scene::Size();
vw::VirtualWindow window(vw::WindowContext(pos, windowSize, sceneSize)
.setBackGroundColor(ColorF{ 0.8, 0.9, 1.0 })
);
while (System::Update())
{
window.draw([&](const s3d::RectF&) {
#pragma region 描画するもの
// テクスチャを描く | Draw a texture
auto r = texture.draw(200, 200);
if (r.mouseOver()) {
r.draw(ColorF(1, 0, 0, 0.1));
}
// テキストを画面の中心に描く | Put a text in the middle of the screen
font(U"Hello, Siv3D!🚀").drawAt(Scene::Center(), Palette::Black);
// サイズをアニメーションさせて絵文字を描く | Draw a texture with animated size
emoji.resized(100 + Periodic::Sine0_1(1s) * 20).drawAt(emojiPos);
// もし [A] キーが押されたら | When [A] key is down
if (KeyA.down()) {
// 選択肢からランダムに選ばれたメッセージをデバッグ表示 | Print a randomly selected text
Print << Sample({ U"Hello!", U"こんにちは", U"你好", U"안녕하세요?" });
}
// もし [Button] が押されたら | When [Button] is pushed
if (SimpleGUI::Button(U"Button", Vec2{ 640, 40 })) {
// 画面内のランダムな場所に座標を移動
// Move the coordinates to a random position in the screen
emojiPos = RandomVec2(Scene::Rect());
}
#pragma endregion
});
}
}
#include "VirtualWindow.hpp"
#include "detail/Component.hpp"
#include "detail/Constants.hpp"
#include "detail/GrabCtrl.hpp"
#include "detail/ScrollCtrl.hpp"
#include "detail/WindowParam.hpp"
#include <Siv3D.hpp>
namespace vw
{
using namespace detail;
WindowContext::WindowContext() :
WindowContext({ 300, 300 })
{}
WindowContext::WindowContext(const s3d::Vec2& _size) :
WindowContext({ 0, 0 }, _size, _size)
{}
WindowContext::WindowContext(const s3d::Vec2& _pos, const s3d::Vec2& _size) :
WindowContext(_pos, _size, _size)
{}
WindowContext::WindowContext(const s3d::Vec2& _pos, const s3d::Vec2& _size, const s3d::Vec2& _sceneSize) :
pos(_pos),
size(_size),
sceneSize(_sceneSize),
backGroundColor(Palette::White),
frameColor(Color(240)),
scrollBarColor(Color(240)),
scrollGripColor(Color(133))
{}
WindowContext& WindowContext::setBackGroundColor(const s3d::Optional<s3d::ColorF>& color)
{
backGroundColor = color;
return *this;
}
WindowContext& WindowContext::setFrameColor(const s3d::Optional<s3d::ColorF>& color)
{
frameColor = color;
return *this;
}
WindowContext& WindowContext::setScrollBarColor(const s3d::ColorF& color)
{
scrollBarColor = color;
return *this;
}
WindowContext& WindowContext::setScrollGripColor(const s3d::ColorF& color)
{
scrollGripColor = color;
return *this;
}
class VirtualWindow::Handle : public WindowParam
{
public:
Handle(const WindowContext& context) :
m_comp(*this)
{
this->pos = context.pos;
this->setSize(context.size);
this->sceneSize = context.sceneSize;
this->scenePos = {};
this->backGroundColor = context.backGroundColor;
this->frameColor = context.frameColor;
this->scrollBarColor = context.scrollBarColor;
this->scrollGripColor = context.scrollGripColor;
this->canResize = context.canResize;
this->isResizeClampSceneSize = context.isResizeClampSceneSize;
this->canMove = context.canMove;
this->canScroll = context.canScroll;
}
void setSize(const s3d::Vec2& _size)
{
if (this->sceneSize.x > _size.x) {
this->size.x = s3d::Max(_size.x, Constants::WinMinSize.x);
} else {
this->size.x = _size.x;
}
if (this->sceneSize.y > _size.y) {
this->size.y = s3d::Max(_size.y, Constants::WinMinSize.x);
} else {
this->size.y = _size.y;
}
}
void setScenePos(const s3d::Vec2& _pos)
{
this->scenePos.x = s3d::Clamp(_pos.x, 0.0, Max(sceneSize.x - size.x, 0.0));
this->scenePos.y = s3d::Clamp(_pos.y, 0.0, Max(sceneSize.y - size.y, 0.0));
}
void setScenePosToBottom()
{
Vec2 nextScenePos = this->scenePos;
nextScenePos.y = Max(sceneSize.y - size.y, 0.0);
this->setScenePos(nextScenePos);
}
bool isScrollBottom() const
{
return scenePos.y >= Max(sceneSize.y - size.y, 0.0);
}
void update()
{
m_comp.grabCtrl().update();
m_comp.scrollCtrl().update();
// Clamp
if (isResizeClampSceneSize) {
size.x = Min(size.x, sceneSize.x);
size.y = Min(size.y, sceneSize.y);
}
this->setScenePos(this->scenePos);
}
s3d::Rect region() const
{
return this->regionF().asRect();
}
const s3d::RectF& regionF() const
{
return WindowParam::region;
}
s3d::ScopedViewport2D startViewport() const
{
return s3d::ScopedViewport2D{ this->region() };
}
s3d::Transformer2D transformer() const
{
return { Mat3x2::Translate(-scenePos), Mat3x2::Translate(pos - scenePos) };
}
const s3d::RectF& draw(std::function<void(const s3d::RectF&)> scene)
{
this->update();
const auto& region = this->regionF();
if (backGroundColor) {
region.draw(*backGroundColor);
}
{
auto t2d = this->transformer();
auto viewport = this->startViewport();
scene(s3d::RectF{ scenePos, size });
}
if (this->canScroll) {
m_comp.scrollCtrl().draw(scrollBarColor, scrollGripColor);
}
if (frameColor) {
region.drawFrame(0, 1, *frameColor);
}
return region;
}
private:
detail::Component m_comp;
};
VirtualWindow::VirtualWindow() :
VirtualWindow({ 100, 100 })
{}
VirtualWindow::VirtualWindow(const s3d::Vec2& size) :
VirtualWindow({ 0, 0 }, size)
{}
VirtualWindow::VirtualWindow(const s3d::Vec2& pos, const s3d::Vec2& size) :
VirtualWindow(pos, size, size)
{}
VirtualWindow::VirtualWindow(
const s3d::Vec2& pos,
const s3d::Vec2& size,
const s3d::Vec2& sceneSize
) :
VirtualWindow(WindowContext{ pos, size, sceneSize })
{}
VirtualWindow::VirtualWindow(const WindowContext& context) :
m_pHandle(std::make_shared<Handle>(context))
{}
VirtualWindow& VirtualWindow::setPos(const s3d::Vec2& pos)
{
m_pHandle->pos = pos;
return *this;
}
VirtualWindow& VirtualWindow::setSize(const s3d::Vec2& size)
{
m_pHandle->setSize(size);
return *this;
}
VirtualWindow& VirtualWindow::setScenePos(const s3d::Vec2& scenePos)
{
m_pHandle->setScenePos(scenePos);
return *this;
}
VirtualWindow& VirtualWindow::setScenePosToBottom()
{
m_pHandle->setScenePosToBottom();
return *this;
}
bool VirtualWindow::isScrollBottom() const
{
return m_pHandle->isScrollBottom();
}
VirtualWindow& VirtualWindow::setSceneSize(const s3d::Vec2& sceneSize)
{
m_pHandle->sceneSize = sceneSize;
return *this;
}
VirtualWindow& VirtualWindow::setBackGroundColor(const s3d::Optional<s3d::ColorF>& color)
{
m_pHandle->backGroundColor = color;
return *this;
}
VirtualWindow& VirtualWindow::setFrameColor(const s3d::Optional<s3d::ColorF>& color)
{
m_pHandle->frameColor = color;
return *this;
}
VirtualWindow& VirtualWindow::setScrollBarColor(const s3d::ColorF& color)
{
m_pHandle->scrollBarColor = color;
return *this;
}
VirtualWindow& VirtualWindow::setScrollGripColor(const s3d::ColorF& color)
{
m_pHandle->scrollGripColor = color;
return *this;
}
const s3d::RectF& VirtualWindow::region() const
{
return m_pHandle->regionF();
}
const s3d::RectF& VirtualWindow::draw(std::function<void(const s3d::RectF&)> scene) const
{
return m_pHandle->draw(std::move(scene));
}
}
#pragma once
#include <memory>
#include <Siv3D/Rect.hpp>
#include <Siv3D/RectF.hpp>
#include <Siv3D/Vector2D.hpp>
#include <Siv3D/Optional.hpp>
namespace vw
{
struct WindowContext
{
public:
WindowContext();
WindowContext(const s3d::Vec2& _size);
WindowContext(const s3d::Vec2& _pos, const s3d::Vec2& _size);
WindowContext(const s3d::Vec2& _pos, const s3d::Vec2& _size, const s3d::Vec2& _sceneSize);
WindowContext& setBackGroundColor(const s3d::Optional<s3d::ColorF>& color);
WindowContext& setFrameColor(const s3d::Optional<s3d::ColorF>& color);
WindowContext& setScrollBarColor(const s3d::ColorF& color);
WindowContext& setScrollGripColor(const s3d::ColorF& color);
WindowContext& setCanResize(bool _canResize)
{
this->canResize = _canResize;
return *this;
}
WindowContext& setIsResizeClampSceneSize(bool _isResizeClampSceneSize)
{
this->isResizeClampSceneSize = _isResizeClampSceneSize;
return *this;
}
WindowContext& setCanMove(bool _canMove)
{
this->canMove = _canMove;
return *this;
}
WindowContext& setCanScroll(bool _canScroll)
{
this->canScroll = _canScroll;
return *this;
}
public:
s3d::Vec2 pos;
s3d::Vec2 size;
s3d::Vec2 sceneSize;
s3d::Optional<ColorF> backGroundColor;
s3d::Optional<ColorF> frameColor;
s3d::ColorF scrollBarColor;
s3d::ColorF scrollGripColor;
bool canResize = true;
bool isResizeClampSceneSize = true;
bool canMove = true;
bool canScroll = true;
};
/// <summary>
/// 仮想ウィンドウ
/// </summary>
class VirtualWindow
{
class Handle;
public:
VirtualWindow();
VirtualWindow(const s3d::Vec2& size);
VirtualWindow(const s3d::Vec2& pos, const s3d::Vec2& size);
VirtualWindow(const s3d::Vec2& pos, const s3d::Vec2& size, const s3d::Vec2& sceneSize);
VirtualWindow(const WindowContext& context);
/// <summary>
/// ウィンドウの座標を設定
/// </summary>
VirtualWindow& setPos(const s3d::Vec2& pos);
/// <summary>
/// ウィンドウのサイズを設定
/// </summary>
VirtualWindow& setSize(const s3d::Vec2& size);
/// <summary>
/// シーンの座標を設定
/// </summary>
VirtualWindow& setScenePos(const s3d::Vec2& scenePos);
/// <summary>
/// シーンの座標を一番下にする
/// </summary>
VirtualWindow& setScenePosToBottom();
/// <summary>
/// スクロールが一番下にあるか
/// </summary>
bool isScrollBottom() const;
/// <summary>
/// シーンのサイズを設定する
/// </summary>
VirtualWindow& setSceneSize(const s3d::Vec2& sceneSize);
VirtualWindow& setBackGroundColor(const s3d::Optional<s3d::ColorF>& color);
VirtualWindow& setFrameColor(const s3d::Optional<s3d::ColorF>& color);
VirtualWindow& setScrollBarColor(const s3d::ColorF& color);
VirtualWindow& setScrollGripColor(const s3d::ColorF& color);
/// <summary>
/// ウィンドウ領域
/// </summary>
const s3d::RectF& region() const;
/// <summary>
/// 描画
/// </summary>
const s3d::RectF& draw(std::function<void(const s3d::RectF&)> scene) const;
private:
std::shared_ptr<Handle> m_pHandle;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment