Source code
Revision control
Copy as Markdown
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
#include "WidgetResizer.h"
#include "WidgetPlacement.h"
#include "Widget.h"
#include "WidgetBorder.h"
#include "Cylinder.h"
#include "Quad.h"
#include "vrb/ConcreteClass.h"
#include "vrb/Color.h"
#include "vrb/CreationContext.h"
#include "vrb/Matrix.h"
#include "vrb/GLError.h"
#include "vrb/Geometry.h"
#include "vrb/Program.h"
#include "vrb/ProgramFactory.h"
#include "vrb/RenderState.h"
#include "vrb/SurfaceTextureFactory.h"
#include "vrb/TextureGL.h"
#include "vrb/TextureSurface.h"
#include "vrb/Toggle.h"
#include "vrb/Transform.h"
#include "vrb/Vector.h"
#include "vrb/VertexArray.h"
namespace crow {
struct ResizeBar;
typedef std::shared_ptr<ResizeBar> ResizeBarPtr;
static const float kBarSize = 0.04f;
#if defined(OCULUSVR)
  static const float kBorder = 0.0f;
#else
  static const float kBorder = kBarSize * 0.15f;
#endif
static const float kHandleRadius = 0.08f;
static const vrb::Vector kDefaultMinResize(1.5f, 1.5f, 0.0f);
static const vrb::Vector kDefaultMaxResize(8.0f, 4.5f, 0.0f);
static vrb::Color kDefaultColor(0x2BD5D5FF);
static vrb::Color kHoverColor(0xf7ce4dff);
static vrb::Color kActiveColor(0xf7ce4dff);
enum class ResizeState {
  Default,
  Hovered,
  Active,
};
template <typename T>
static void UpdateResizeMaterial(const T& aTarget, ResizeState aState) {
  vrb::Color ambient(0.5f, 0.5f, 0.5f, 1.0f);
  vrb::Color diffuse = kDefaultColor;
  if (aState == ResizeState::Hovered) {
    diffuse = kHoverColor;
  } else if (aState == ResizeState::Active) {
    diffuse = kActiveColor;
  }
  aTarget->SetMaterial(ambient, diffuse, vrb::Color(0.0f, 0.0f, 0.0f), 0.0f);
}
struct ResizeBar {
  static ResizeBarPtr Create(vrb::CreationContextPtr& aContext, const vrb::Vector& aCenter, const vrb::Vector& aScale, const device::EyeRect& aBorderRect, const WidgetBorder::Mode aMode) {
    auto result = std::make_shared<ResizeBar>();
    result->center = aCenter;
    result->scale = aScale;
    vrb::Vector size(kBarSize, kBarSize, 0.0f);
    result->border = WidgetBorder::Create(aContext, size, kBorder, aBorderRect, aMode);
    result->resizeState = ResizeState::Default;
    result->UpdateMaterial();
    return result;
  }
  void SetResizeState(ResizeState aState) {
    if (resizeState != aState) {
      resizeState = aState;
      UpdateMaterial();
    }
  }
  void UpdateMaterial() {
    vrb::Color color = kDefaultColor;
    if (resizeState == ResizeState::Hovered) {
      color = kHoverColor;
    } else if (resizeState == ResizeState::Active) {
      color = kActiveColor;
    }
    border->SetColor(color);
  }
  void SetTransform(const vrb::Matrix& aTransform) {
    border->GetTransformNode()->SetTransform(aTransform);
  }
  vrb::Vector center;
  vrb::Vector scale;
  WidgetBorderPtr border;
  ResizeState resizeState;
};
struct ResizeHandle;
typedef std::shared_ptr<ResizeHandle> ResizeHandlePtr;
struct ResizeHandle {
  enum class ResizeMode {
    Vertical,
    Horizontal,
    Both
  };
  static ResizeHandlePtr Create(vrb::CreationContextPtr& aContext, const vrb::Vector& aCenter, ResizeMode aResizeMode, const std::vector<ResizeBarPtr>& aAttachedBars) {
    auto result = std::make_shared<ResizeHandle>();
    result->center = aCenter;
    result->resizeMode = aResizeMode;
    result->attachedBars = aAttachedBars;
    vrb::Vector max(kHandleRadius, kHandleRadius, 0.0f);
    result->geometry = ResizeHandle::CreateGeometry(aContext);
    result->transform = vrb::Transform::Create(aContext);
    result->transform->AddNode(result->geometry);
    result->root = vrb::Toggle::Create(aContext);
    result->root->AddNode(result->transform);
    result->resizeState = ResizeState ::Default;
    UpdateResizeMaterial(result->geometry->GetRenderState(), result->resizeState);
    return result;
  }
  void SetResizeState(ResizeState aState) {
    if (resizeState != aState) {
      resizeState = aState;
      UpdateResizeMaterial(geometry->GetRenderState(), resizeState);
    }
    for (const ResizeBarPtr& bar: attachedBars) {
      bar->SetResizeState(aState);
    }
  }
  static vrb::GeometryPtr CreateGeometry(vrb::CreationContextPtr& aContext) {
    vrb::VertexArrayPtr array = vrb::VertexArray::Create(aContext);
    array->AppendVertex(vrb::Vector(0.0f, 0.0f, 0.0f));
    array->AppendNormal(vrb::Vector(0.0f, 0.0f, 1.0f));
    vrb::Color solid(1.0f, 1.0f, 1.0f, 1.0f);
    vrb::Color border(1.0f, 1.0f, 1.0f, 0.0f);
    array->AppendColor(solid);
    std::vector<int> indices;
    std::vector<int> normalIndices;
    const int kSides = 30;
    double delta = 2.0 * M_PI / kSides;
    for (int i = 0; i < kSides; ++i) {
      const double angle = delta * i;
      array->AppendVertex(vrb::Vector(kHandleRadius * (float)cos(angle), kHandleRadius * (float)sin(angle), 0.0f));
      array->AppendColor(solid);
      if (i > 0) {
        indices.push_back(1);
        indices.push_back(i + 1);
        indices.push_back(i + 2);
        normalIndices.push_back(1);
        normalIndices.push_back(1);
        normalIndices.push_back(1);
      }
    }
    indices.push_back(1);
    indices.push_back(array->GetVertexCount());
    indices.push_back(2);
    normalIndices.push_back(1);
    normalIndices.push_back(1);
    normalIndices.push_back(1);
    vrb::GeometryPtr geometry = vrb::Geometry::Create(aContext);
    vrb::ProgramPtr program = aContext->GetProgramFactory()->CreateProgram(aContext, vrb::FeatureVertexColor);
    vrb::RenderStatePtr state = vrb::RenderState::Create(aContext);
    state->SetProgram(program);
    state->SetLightsEnabled(false);
    geometry->SetVertexArray(array);
    geometry->SetRenderState(state);
    geometry->AddFace(indices, indices, normalIndices);
    if (kBorder > 0.0f) {
      int lastCircleIndex = array->GetVertexCount();
      int borderIndex = array->GetVertexCount() + 1;
      for (int i = 0; i < kSides; ++i) {
        const double angle = delta * i;
        const float r = kHandleRadius + kBorder;
        array->AppendVertex(vrb::Vector(r * (float)cos(angle), r * (float)sin(angle), 0.0f));
        array->AppendColor(border);
        if (i > 0) {
          indices.clear();
          normalIndices.clear();
          indices.push_back(i + 2);
          indices.push_back(i + 1);
          indices.push_back(borderIndex);
          indices.push_back(++borderIndex);
          normalIndices.push_back(1);
          normalIndices.push_back(1);
          normalIndices.push_back(1);
          normalIndices.push_back(1);
          geometry->AddFace(indices, indices, normalIndices);
        }
      }
      indices.clear();
      normalIndices.clear();
      indices.push_back(2);
      indices.push_back(lastCircleIndex);
      indices.push_back(borderIndex);
      indices.push_back(lastCircleIndex + 1);
      normalIndices.push_back(1);
      normalIndices.push_back(1);
      normalIndices.push_back(1);
      normalIndices.push_back(1);
      geometry->AddFace(indices, indices, normalIndices);
    }
    return geometry;
  }
  void SetVisible(const bool aVisible) {
    if (visible != aVisible) {
      root->ToggleAll(aVisible);
      visible = aVisible;
    }
  }
  vrb::Vector center;
  ResizeMode resizeMode;
  std::vector<ResizeBarPtr> attachedBars;
  vrb::GeometryPtr geometry;
  vrb::TogglePtr root;
  vrb::TransformPtr transform;
  ResizeState resizeState;
  float touchRatio;
  bool visible = true;
};
struct WidgetResizer::State {
  vrb::CreationContextWeak context;
  Widget * widget;
  vrb::Vector min;
  vrb::Vector max;
  vrb::Vector resizeStartMin;
  vrb::Vector resizeStartMax;
  vrb::Vector currentMin;
  vrb::Vector currentMax;
  vrb::Vector pointerOffset;
  vrb::Vector maxSize;
  vrb::Vector minSize;
  bool resizing;
  vrb::TogglePtr root;
  vrb::TransformPtr transform;
  std::vector<ResizeHandlePtr> resizeHandles;
  std::vector<ResizeBarPtr> resizeBars;
  ResizeHandlePtr activeHandle;
  bool wasPressed;
  State()
      : widget(nullptr)
      , resizing(false)
      , wasPressed(false)
  {}
  void Initialize() {
    vrb::CreationContextPtr create = context.lock();
    if (!create) {
      return;
    }
    root = vrb::Toggle::Create(create);
    transform = vrb::Transform::Create(create);
    root->AddNode(transform);
    currentMin = min;
    currentMax = max;
    maxSize = kDefaultMaxResize;
    minSize = kDefaultMinResize;
    vrb::Vector horizontalSize(0.0f, 0.5f, 0.0f);
    vrb::Vector verticalSize(0.5f, 0.0f, 0.0f);
    device::EyeRect horizontalBorder(0.0f, kBorder, 0.0f, kBorder);
    device::EyeRect verticalBorder(kBorder, 0.0f, kBorder, 0.0f);
    WidgetBorder::Mode mode = widget->GetCylinder() ? WidgetBorder::Mode::Cylinder : WidgetBorder::Mode::Quad;
    ResizeBarPtr leftTop = CreateResizeBar(vrb::Vector(0.0f, 0.75f, 0.0f), horizontalSize, verticalBorder, WidgetBorder::Mode::Quad);
    ResizeBarPtr leftBottom = CreateResizeBar(vrb::Vector(0.0f, 0.25f, 0.0f), horizontalSize, verticalBorder, WidgetBorder::Mode::Quad);
    ResizeBarPtr rightTop = CreateResizeBar(vrb::Vector(1.0f, 0.75f, 0.0f), horizontalSize, verticalBorder, WidgetBorder::Mode::Quad);
    ResizeBarPtr rightBottom = CreateResizeBar(vrb::Vector(1.0f, 0.25f, 0.0f), horizontalSize, verticalBorder, WidgetBorder::Mode::Quad);
    ResizeBarPtr topLeft = CreateResizeBar(vrb::Vector(0.25f, 1.0f, 0.0f), verticalSize, horizontalBorder, mode);
    ResizeBarPtr topRight = CreateResizeBar(vrb::Vector(0.75f, 1.0f, 0.0f), verticalSize, horizontalBorder, mode);
    //ResizeBarPtr bottomLeft = CreateResizeBar(vrb::Vector(0.25f, 0.0f, 0.0f), verticalSize, mode);
    //ResizeBarPtr bottomRight = CreateResizeBar(vrb::Vector(0.75f, 0.0f, 0.0f), verticalSize, mode);
    ResizeBarPtr bottom = CreateResizeBar(vrb::Vector(0.5f, 0.0f, 0.0f), vrb::Vector(1.0f, 0.0f, 0.0f), horizontalBorder, mode);
    //ResizeBarPtr bottomLeftCorner = CreateResizeBar(vrb::Vector(0.0f, 0.0f, 0.0f), vrb::Vector(0.0f, 0.0f, 0.0f), device::EyeRect(kBorder, kBorder, 0.0f, 0.0f), ResizeBar::Mode::Quad);
    //ResizeBarPtr bottomRightCorner = CreateResizeBar(vrb::Vector(1.0f, 0.0f, 0.0f), vrb::Vector(0.0f, 0.0f, 0.0f), device::EyeRect(.0f, 0.0f, kBorder, kBorder), ResizeBar::Mode::Quad);
    CreateResizeHandle(vrb::Vector(0.0f, 1.0f, 0.0f), ResizeHandle::ResizeMode::Both, {leftTop, topLeft});
    CreateResizeHandle(vrb::Vector(1.0f, 1.0f, 0.0f), ResizeHandle::ResizeMode::Both, {rightTop, topRight});
    //CreateResizeHandle(vrb::Vector(0.0f, 0.0f, 0.0f), ResizeHandle::ResizeMode::Both, {leftBottom, bottomLeft});
    //CreateResizeHandle(vrb::Vector(1.0f, 0.0f, 0.0f), ResizeHandle::ResizeMode::Both, {rightBottom, bottomRight}, 1.0f);
    CreateResizeHandle(vrb::Vector(0.5f, 1.0f, 0.0f), ResizeHandle::ResizeMode::Horizontal, {topLeft, topRight});
    //CreateResizeHandle(vrb::Vector(0.5f, 0.0f, 0.0f), ResizeHandle::ResizeMode::Horizontal, {bottomLeft, bottomRight});
    CreateResizeHandle(vrb::Vector(0.0f, 0.5f, 0.0f), ResizeHandle::ResizeMode::Vertical, {leftTop, leftBottom});
    CreateResizeHandle(vrb::Vector(1.0f, 0.5f, 0.0f), ResizeHandle::ResizeMode::Vertical, {rightTop, rightBottom});
    Layout();
  }
  ResizeBarPtr CreateResizeBar(const vrb::Vector& aCenter, vrb::Vector aScale, const device::EyeRect& aBorder, const WidgetBorder::Mode aMode) {
    vrb::CreationContextPtr create = context.lock();
    if (!create) {
      return nullptr;
    }
    ResizeBarPtr result = ResizeBar::Create(create, aCenter, aScale, aBorder, aMode);
    resizeBars.push_back(result);
    transform->AddNode(result->border->GetTransformNode());
    return result;
  }
  ResizeHandlePtr CreateResizeHandle(const vrb::Vector& aCenter, ResizeHandle::ResizeMode aResizeMode, const std::vector<ResizeBarPtr>& aBars, const float aTouchRatio = 2.0f) {
    vrb::CreationContextPtr create = context.lock();
    if (!create) {
      return nullptr;
    }
    ResizeHandlePtr result = ResizeHandle::Create(create, aCenter, aResizeMode, aBars);
    result->touchRatio = aTouchRatio;
    resizeHandles.push_back(result);
    transform->InsertNode(result->root, 0);
    return result;
  }
  float WorldWidth() const {
    return max.x() - min.x();
  }
  float WorldHeight() const {
    return max.y() - min.y();
  }
  vrb::Vector ProjectPoint(const vrb::Vector& aWorldPoint) const {
    if (widget->GetCylinder()) {
      return widget->GetCylinder()->ProjectPointToQuad(aWorldPoint, GetAnchorX(), widget->GetCylinderDensity(), min, max);
    } else {
      // For quads just convert to world point to local point.
      vrb::Matrix modelView = widget->GetTransformNode()->GetWorldTransform().AfineInverse();
      return modelView.MultiplyPosition(aWorldPoint);
    }
  }
  void LayoutQuad() {
    const float width = WorldWidth();
    const float height = WorldHeight();
    for (ResizeBarPtr& bar: resizeBars) {
      float targetWidth = bar->scale.x() > 0.0f ? (bar->scale.x() * fabsf(width)) + kBarSize : kBarSize;
      float targetHeight = bar->scale.y() > 0.0f ? (bar->scale.y() * fabs(height)) + kBarSize : kBarSize;
      vrb::Matrix matrix = vrb::Matrix::Position(vrb::Vector(min.x() + width * bar->center.x(), min.y() + height * bar->center.y(), 0.005f));
      matrix.ScaleInPlace(vrb::Vector(targetWidth / kBarSize, targetHeight / kBarSize, 1.0f));
      bar->SetTransform(matrix);
    }
    for (ResizeHandlePtr& handle: resizeHandles) {
      vrb::Matrix matrix = vrb::Matrix::Position(vrb::Vector(min.x() + width * handle->center.x(), min.y() + height * handle->center.y(), 0.006f));
      handle->transform->SetTransform(matrix);
    }
  }
  void LayoutCylinder() {
    const float width = currentMax.x() - currentMin.x();
    const float height = currentMax.y() - currentMin.y();
    const float sx = width / WorldWidth();
    const float radius = widget->GetCylinder()->GetTransformNode()->GetTransform().GetScale().x() - kBarSize * 0.5f;
    const float theta = widget->GetCylinder()->GetCylinderTheta() * sx;
    int32_t textureWidth, textureHeight;
    widget->GetCylinder()->GetTextureSize(textureWidth, textureHeight);
    vrb::Matrix modelView = widget->GetTransformNode()->GetWorldTransform().AfineInverse();
    // Delta for x anchor point != 0.5f.
    float centerX = 0.0f;
    const float anchorX = GetAnchorX();
    if (anchorX == 1.0f) {
      centerX = min.x() + width * 0.5f;
    } else if (anchorX == 0.0f) {
      centerX = max.x() - width * 0.5f;
    }
    const float perimeter = 2.0f * radius * (float)M_PI;
    float angleDelta = centerX / perimeter * 2.0f * (float)M_PI;
    for (ResizeBarPtr& bar: resizeBars) {
      float targetWidth = bar->scale.x() > 0.0f ? (bar->scale.x() * fabsf(width)) + kBarSize : kBarSize;
      float targetHeight = bar->scale.y() > 0.0f ? (bar->scale.y() * fabs(height)) + kBarSize : kBarSize;
      float pointerAngle = (float)M_PI * 0.5f + theta * 0.5f - theta * bar->center.x() + angleDelta;
      vrb::Matrix rotation = vrb::Matrix::Rotation(vrb::Vector(-cosf(pointerAngle), 0.0f, sinf(pointerAngle)));
      if (bar->border->GetCylinder()) {
        bar->border->GetCylinder()->SetCylinderTheta(theta * bar->scale.x());
        vrb::Matrix translation = vrb::Matrix::Position(vrb::Vector(0.0f, min.y() + height * bar->center.y(), radius));
        vrb::Matrix scale = vrb::Matrix::Identity();
        scale.ScaleInPlace(vrb::Vector(radius, 1.0f, radius));
        bar->SetTransform(translation.PostMultiply(scale).PostMultiply(rotation));
      } else {
        vrb::Matrix translation = vrb::Matrix::Position(vrb::Vector(radius * cosf(pointerAngle),
                                                        min.y() + height * bar->center.y(),
                                                        radius - radius * sinf(pointerAngle)));
        vrb::Matrix scale = vrb::Matrix::Identity();
        scale.ScaleInPlace(vrb::Vector(targetWidth / kBarSize, targetHeight / kBarSize, 1.0f));
        bar->SetTransform(translation.PostMultiply(scale).PostMultiply(rotation));
      }
    }
    for (ResizeHandlePtr& handle: resizeHandles) {
      const float pointerAngle = (float)M_PI * 0.5f + theta * 0.5f - theta * handle->center.x() + angleDelta;
      vrb::Matrix translation = vrb::Matrix::Position(vrb::Vector(radius * cosf(pointerAngle),
                                                      min.y() + height * handle->center.y(),
                                                      radius - radius * sinf(pointerAngle)));
      vrb::Matrix rotation = vrb::Matrix::Rotation(vrb::Vector(-cosf(pointerAngle), 0.0f, sinf(pointerAngle)));
      handle->transform->SetTransform(translation.PostMultiply(rotation));
    }
  }
  void UpdateVisibleHandles() {
    float anchorX = GetAnchorX();
    for (ResizeHandlePtr & handle: resizeHandles) {
      handle->SetVisible(handle->center.x() == 0.5f || (handle->center.x() != anchorX));
    }
  }
  float GetAnchorX() const {
    if (widget && widget->GetPlacement()) {
      return  widget->GetPlacement()->anchor.x();
    }
    return 0.5f;
  }
  void Layout() {
    UpdateVisibleHandles();
    if (widget->GetCylinder()) {
      LayoutCylinder();
    } else {
      LayoutQuad();
    }
  }
  ResizeHandlePtr GetIntersectingHandler(const vrb::Vector& point) {
    for (const ResizeHandlePtr& handle: resizeHandles) {
      if (!handle->visible) {
        continue;
      }
      vrb::Vector worldCenter(min.x() + WorldWidth() * handle->center.x(), min.y() + WorldHeight() * handle->center.y(), 0.0f);
      float distance = (point - worldCenter).Magnitude();
      if (distance < kHandleRadius * handle->touchRatio) {
        return handle;
      }
    }
    return nullptr;
  }
  void HandleResize(const vrb::Vector& aPoint) {
    if (!activeHandle) {
      return;
    }
    const vrb::Vector point = aPoint - pointerOffset;
    float originalWidth = fabsf(resizeStartMax.x() - resizeStartMin.x());
    float originalHeight = fabsf(resizeStartMax.y() - resizeStartMin.y());
    float originalAspect = originalWidth / originalHeight;
    float width = fabsf(point.x()) * 2.0f;
    if (widget->GetPlacement()->anchor.x() == 1.0f) {
      width = fabsf(max.x() - point.x());
    } else if (widget->GetPlacement()->anchor.x() == 0.0f) {
      width = fabsf(point.x() - min.x());
    }
    float height = fabsf(point.y() - min.y());
    // Calculate resize based on resize mode
    bool keepAspect = false;
    if (activeHandle->resizeMode == ResizeHandle::ResizeMode::Vertical) {
      height = originalHeight;
    } else if (activeHandle->resizeMode == ResizeHandle::ResizeMode::Horizontal) {
      width = originalWidth;
    } else {
      width = fmaxf(width, height * originalAspect);
      height = width / originalAspect;
      keepAspect = true;
    }
    // Clamp to max and min resize sizes
    width = fmaxf(fminf(width, maxSize.x()), minSize.x());
    height = fmaxf(fminf(height, maxSize.y()), minSize.y());
    if (keepAspect) {
      height = width / originalAspect;
    }
    currentMin = vrb::Vector(-width * 0.5f, -height * 0.5f, 0.0f);
    currentMax = vrb::Vector(width * 0.5f, height * 0.5f, 0.0f);
    // Reset world min and max points with the new resize values
    if (!widget->GetCylinder()) {
      min = currentMin;
      max = currentMax;
    }
    Layout();
  }
};
WidgetResizerPtr
WidgetResizer::Create(vrb::CreationContextPtr& aContext, Widget * aWidget) {
  WidgetResizerPtr result = std::make_shared<vrb::ConcreteClass<WidgetResizer, WidgetResizer::State> >(aContext);
  aWidget->GetWidgetMinAndMax(result->m.min, result->m.max);
  result->m.widget = aWidget;
  result->m.Initialize();
  return result;
}
vrb::NodePtr
WidgetResizer::GetRoot() const {
  return m.root;
}
void
WidgetResizer::SetSize(const vrb::Vector& aMin, const vrb::Vector& aMax) {
  m.min = aMin;
  m.max = aMax;
  m.currentMin = aMin;
  m.currentMax = aMax;
  m.Layout();
}
void
WidgetResizer::SetResizeLimits(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize) {
  m.maxSize = aMaxSize;
  m.minSize = aMinSize;
}
void
WidgetResizer::ToggleVisible(bool aVisible) {
  m.root->ToggleAll(aVisible);
}
bool
WidgetResizer::TestIntersection(const vrb::Vector& aWorldPoint) const {
  if (m.activeHandle) {
    return true;
  }
  const vrb::Vector point = m.ProjectPoint(aWorldPoint);
  vrb::Vector extraMin = vrb::Vector(m.min.x() - kBarSize * 0.5f, m.min.y() - kBarSize * 0.5f, 0.0f);
  vrb::Vector extraMax = vrb::Vector(m.max.x() + kBarSize * 0.5f, m.max.y() + kBarSize * 0.5f, 0.0f);
  if ((point.x() >= extraMin.x()) && (point.y() >= extraMin.y()) &&(point.z() >= (extraMin.z() - 0.1f)) &&
      (point.x() <= extraMax.x()) && (point.y() <= extraMax.y()) &&(point.z() <= (extraMax.z() + 0.1f))) {
    return true;
  }
  return m.GetIntersectingHandler(point).get() != nullptr;
}
void
WidgetResizer::HandleResizeGestures(const vrb::Vector& aWorldPoint, bool aPressed, bool& aResized, bool &aResizeEnded) {
  const vrb::Vector point = m.ProjectPoint(aWorldPoint);
  for (const ResizeHandlePtr& handle: m.resizeHandles) {
    handle->SetResizeState(ResizeState::Default);
  }
  aResized = false;
  aResizeEnded = false;
  if (aPressed && !m.wasPressed) {
    // Handle resize handle click
    m.activeHandle = m.GetIntersectingHandler(point);
    if (m.activeHandle) {
      m.resizeStartMin = m.min;
      m.resizeStartMax = m.max;
      m.currentMin = m.min;
      m.currentMax = m.max;
      m.activeHandle->SetResizeState(ResizeState::Active);
      vrb::Vector center(m.min.x() + m.WorldWidth() * m.activeHandle->center.x(),
                         m.min.y() + m.WorldHeight() * m.activeHandle->center.y(), 0.0f);
      m.pointerOffset = point - center;
    }
  } else if (!aPressed && m.wasPressed) {
    // Handle resize handle unclick
    if (m.activeHandle) {
      m.activeHandle->SetResizeState(ResizeState::Hovered);
      m.min = m.currentMin;
      m.max = m.currentMax;
      aResizeEnded = true;
    }
    m.activeHandle.reset();
  } else if (aPressed && m.activeHandle) {
    // Handle resize gesture
    m.activeHandle->SetResizeState(ResizeState::Active);
    m.HandleResize(point);
    aResized = true;
  } else if (!aPressed) {
    // Handle hover
    ResizeHandlePtr handle = m.GetIntersectingHandler(point);
    if (handle) {
      handle->SetResizeState(ResizeState::Hovered);
    }
  }
  m.wasPressed = aPressed;
}
void
WidgetResizer::HoverExitResize() {
  for (const ResizeHandlePtr& handle: m.resizeHandles) {
    handle->SetResizeState(ResizeState::Default);
  }
  m.wasPressed = false;
}
const vrb::Vector&
WidgetResizer::GetResizeMin() const {
  return m.min;
}
const vrb::Vector&
WidgetResizer::GetResizeMax() const {
  return m.max;
}
bool
WidgetResizer::IsActive() const {
  return m.activeHandle && m.activeHandle->resizeState == ResizeState::Active;
}
void
WidgetResizer::SetTransform(const vrb::Matrix &aTransform){
  m.transform->SetTransform(aTransform);
}
Widget*
WidgetResizer::GetWidget() const {
  return m.widget;
}
WidgetResizer::WidgetResizer(State& aState, vrb::CreationContextPtr& aContext) : m(aState) {
  m.context = aContext;
}
} // namespace crow