Typedef pointers are unsafe. If you do: typedef struct bla *bla_t; then you cannot use it as a constant, such as: const bla_t, because that constant will be to the pointer itself rather than to the underlying data. I admit this was a fundamental mistake that must be corrected. All typedefs that were pointer types will now have their pointers removed from the type itself, and the pointers will be used when they are actually used as variables/parameters/returns instead. This does not break ABI though, which is pretty nice.
751 lines
19 KiB
C++
751 lines
19 KiB
C++
#include <QGuiApplication>
|
|
#include <QMouseEvent>
|
|
|
|
#include <cmath>
|
|
#include <graphics/vec4.h>
|
|
#include <graphics/matrix4.h>
|
|
#include "window-basic-preview.hpp"
|
|
#include "window-basic-main.hpp"
|
|
#include "obs-app.hpp"
|
|
|
|
#define HANDLE_RADIUS 4.0f
|
|
#define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f)
|
|
#define CLAMP_DISTANCE 10.0f
|
|
|
|
/* TODO: make C++ math classes and clean up code here later */
|
|
|
|
OBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags)
|
|
: OBSQTDisplay(parent, flags)
|
|
{
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
float pixelRatio = main->devicePixelRatio();
|
|
float scale = pixelRatio / main->previewScale;
|
|
vec2 pos;
|
|
vec2_set(&pos,
|
|
(float(event->x()) - main->previewX / pixelRatio) * scale,
|
|
(float(event->y()) - main->previewY / pixelRatio) * scale);
|
|
|
|
return pos;
|
|
}
|
|
|
|
struct SceneFindData {
|
|
const vec2 &pos;
|
|
OBSSceneItem item;
|
|
bool selectBelow;
|
|
|
|
SceneFindData(const SceneFindData &) = delete;
|
|
SceneFindData(SceneFindData &&) = delete;
|
|
SceneFindData& operator=(const SceneFindData &) = delete;
|
|
SceneFindData& operator=(SceneFindData &&) = delete;
|
|
|
|
inline SceneFindData(const vec2 &pos_, bool selectBelow_)
|
|
: pos (pos_),
|
|
selectBelow (selectBelow_)
|
|
{}
|
|
};
|
|
|
|
static bool SceneItemHasVideo(obs_sceneitem_t *item)
|
|
{
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
uint32_t flags = obs_source_get_output_flags(source);
|
|
return (flags & OBS_SOURCE_VIDEO) != 0;
|
|
}
|
|
|
|
static bool FindItemAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
|
|
void *param)
|
|
{
|
|
SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
|
|
matrix4 transform;
|
|
vec3 transformedPos;
|
|
vec3 pos3;
|
|
|
|
if (!SceneItemHasVideo(item))
|
|
return true;
|
|
|
|
vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
|
|
|
|
obs_sceneitem_get_box_transform(item, &transform);
|
|
|
|
matrix4_inv(&transform, &transform);
|
|
vec3_transform(&transformedPos, &pos3, &transform);
|
|
|
|
if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
|
|
transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
|
|
if (data->selectBelow && obs_sceneitem_selected(item)) {
|
|
if (data->item)
|
|
return false;
|
|
else
|
|
data->selectBelow = false;
|
|
}
|
|
|
|
data->item = item;
|
|
}
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
static vec3 GetTransformedPos(float x, float y, const matrix4 &mat)
|
|
{
|
|
vec3 result;
|
|
vec3_set(&result, x, y, 0.0f);
|
|
vec3_transform(&result, &result, &mat);
|
|
return result;
|
|
}
|
|
|
|
static vec3 GetTransformedPosScaled(float x, float y, const matrix4 &mat,
|
|
float scale)
|
|
{
|
|
vec3 result;
|
|
vec3_set(&result, x, y, 0.0f);
|
|
vec3_transform(&result, &result, &mat);
|
|
vec3_mulf(&result, &result, scale);
|
|
return result;
|
|
}
|
|
|
|
static inline vec2 GetOBSScreenSize()
|
|
{
|
|
obs_video_info ovi;
|
|
vec2 size;
|
|
vec2_zero(&size);
|
|
|
|
if (obs_get_video_info(&ovi)) {
|
|
size.x = float(ovi.base_width);
|
|
size.y = float(ovi.base_height);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
vec3 OBSBasicPreview::GetScreenSnapOffset(const vec3 &tl, const vec3 &br)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
vec2 screenSize = GetOBSScreenSize();
|
|
vec3 clampOffset;
|
|
|
|
vec3_zero(&clampOffset);
|
|
|
|
const float clampDist = CLAMP_DISTANCE / main->previewScale;
|
|
|
|
if (fabsf(tl.x) < clampDist)
|
|
clampOffset.x = -tl.x;
|
|
if (fabsf(clampOffset.x) < EPSILON &&
|
|
fabsf(screenSize.x - br.x) < clampDist)
|
|
clampOffset.x = screenSize.x - br.x;
|
|
|
|
if (fabsf(tl.y) < clampDist)
|
|
clampOffset.y = -tl.y;
|
|
if (fabsf(clampOffset.y) < EPSILON &&
|
|
fabsf(screenSize.y - br.y) < clampDist)
|
|
clampOffset.y = screenSize.y - br.y;
|
|
|
|
return clampOffset;
|
|
}
|
|
|
|
OBSSceneItem OBSBasicPreview::GetItemAtPos(const vec2 &pos, bool selectBelow)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
OBSScene scene = main->GetCurrentScene();
|
|
if (!scene)
|
|
return OBSSceneItem();
|
|
|
|
SceneFindData data(pos, selectBelow);
|
|
obs_scene_enum_items(scene, FindItemAtPos, &data);
|
|
return data.item;
|
|
}
|
|
|
|
static bool CheckItemSelected(obs_scene_t *scene, obs_sceneitem_t *item,
|
|
void *param)
|
|
{
|
|
SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
|
|
matrix4 transform;
|
|
vec3 transformedPos;
|
|
vec3 pos3;
|
|
|
|
if (!SceneItemHasVideo(item))
|
|
return true;
|
|
|
|
vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
|
|
|
|
obs_sceneitem_get_box_transform(item, &transform);
|
|
|
|
matrix4_inv(&transform, &transform);
|
|
vec3_transform(&transformedPos, &pos3, &transform);
|
|
|
|
if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
|
|
transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
|
|
if (obs_sceneitem_selected(item)) {
|
|
data->item = item;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
bool OBSBasicPreview::SelectedAtPos(const vec2 &pos)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
OBSScene scene = main->GetCurrentScene();
|
|
if (!scene)
|
|
return false;
|
|
|
|
SceneFindData data(pos, false);
|
|
obs_scene_enum_items(scene, CheckItemSelected, &data);
|
|
return !!data.item;
|
|
}
|
|
|
|
struct HandleFindData {
|
|
const vec2 &pos;
|
|
const float scale;
|
|
|
|
OBSSceneItem item;
|
|
ItemHandle handle = ItemHandle::None;
|
|
|
|
HandleFindData(const HandleFindData &) = delete;
|
|
HandleFindData(HandleFindData &&) = delete;
|
|
HandleFindData& operator=(const HandleFindData &) = delete;
|
|
HandleFindData& operator=(HandleFindData &&) = delete;
|
|
|
|
inline HandleFindData(const vec2 &pos_, float scale_)
|
|
: pos (pos_),
|
|
scale (scale_)
|
|
{}
|
|
};
|
|
|
|
static bool FindHandleAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
|
|
void *param)
|
|
{
|
|
if (!obs_sceneitem_selected(item))
|
|
return true;
|
|
|
|
HandleFindData *data = reinterpret_cast<HandleFindData*>(param);
|
|
matrix4 transform;
|
|
vec3 pos3;
|
|
float closestHandle = HANDLE_SEL_RADIUS;
|
|
|
|
vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
|
|
|
|
obs_sceneitem_get_box_transform(item, &transform);
|
|
|
|
auto TestHandle = [&] (float x, float y, ItemHandle handle)
|
|
{
|
|
vec3 handlePos = GetTransformedPosScaled(x, y, transform,
|
|
data->scale);
|
|
|
|
float dist = vec3_dist(&handlePos, &pos3);
|
|
if (dist < HANDLE_SEL_RADIUS) {
|
|
if (dist < closestHandle) {
|
|
closestHandle = dist;
|
|
data->handle = handle;
|
|
data->item = item;
|
|
}
|
|
}
|
|
};
|
|
|
|
TestHandle(0.0f, 0.0f, ItemHandle::TopLeft);
|
|
TestHandle(0.5f, 0.0f, ItemHandle::TopCenter);
|
|
TestHandle(1.0f, 0.0f, ItemHandle::TopRight);
|
|
TestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);
|
|
TestHandle(1.0f, 0.5f, ItemHandle::CenterRight);
|
|
TestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);
|
|
TestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);
|
|
TestHandle(1.0f, 1.0f, ItemHandle::BottomRight);
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
static vec2 GetItemSize(obs_sceneitem_t *item)
|
|
{
|
|
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);
|
|
vec2 size;
|
|
|
|
if (boundsType != OBS_BOUNDS_NONE) {
|
|
obs_sceneitem_get_bounds(item, &size);
|
|
} else {
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
vec2 scale;
|
|
|
|
obs_sceneitem_get_scale(item, &scale);
|
|
size.x = float(obs_source_get_width(source)) * scale.x;
|
|
size.y = float(obs_source_get_height(source)) * scale.y;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
OBSScene scene = main->GetCurrentScene();
|
|
if (!scene)
|
|
return;
|
|
|
|
HandleFindData data(pos, main->previewScale / main->devicePixelRatio());
|
|
obs_scene_enum_items(scene, FindHandleAtPos, &data);
|
|
|
|
stretchItem = std::move(data.item);
|
|
stretchHandle = data.handle;
|
|
|
|
if (stretchHandle != ItemHandle::None) {
|
|
matrix4 boxTransform;
|
|
vec3 itemUL;
|
|
float itemRot;
|
|
|
|
stretchItemSize = GetItemSize(stretchItem);
|
|
|
|
obs_sceneitem_get_box_transform(stretchItem, &boxTransform);
|
|
itemRot = obs_sceneitem_get_rot(stretchItem);
|
|
vec3_from_vec4(&itemUL, &boxTransform.t);
|
|
|
|
/* build the item space conversion matrices */
|
|
matrix4_identity(&itemToScreen);
|
|
matrix4_rotate_aa4f(&itemToScreen, &itemToScreen,
|
|
0.0f, 0.0f, 1.0f, RAD(itemRot));
|
|
matrix4_translate3f(&itemToScreen, &itemToScreen,
|
|
itemUL.x, itemUL.y, 0.0f);
|
|
|
|
matrix4_identity(&screenToItem);
|
|
matrix4_translate3f(&screenToItem, &screenToItem,
|
|
-itemUL.x, -itemUL.y, 0.0f);
|
|
matrix4_rotate_aa4f(&screenToItem, &screenToItem,
|
|
0.0f, 0.0f, 1.0f, RAD(-itemRot));
|
|
}
|
|
}
|
|
|
|
void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
float pixelRatio = main->devicePixelRatio();
|
|
float x = float(event->x()) - main->previewX / pixelRatio;
|
|
float y = float(event->y()) - main->previewY / pixelRatio;
|
|
|
|
if (event->button() != Qt::LeftButton)
|
|
return;
|
|
|
|
mouseDown = true;
|
|
|
|
vec2_set(&startPos, x, y);
|
|
GetStretchHandleData(startPos);
|
|
|
|
vec2_divf(&startPos, &startPos, main->previewScale / pixelRatio);
|
|
startPos.x = std::round(startPos.x);
|
|
startPos.y = std::round(startPos.y);
|
|
|
|
mouseOverItems = SelectedAtPos(startPos);
|
|
vec2_zero(&lastMoveOffset);
|
|
}
|
|
|
|
static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
|
|
{
|
|
obs_sceneitem_t *selectedItem =
|
|
reinterpret_cast<obs_sceneitem_t*>(param);
|
|
obs_sceneitem_select(item, (selectedItem == item));
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
void OBSBasicPreview::DoSelect(const vec2 &pos)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
OBSScene scene = main->GetCurrentScene();
|
|
OBSSceneItem item = GetItemAtPos(pos, true);
|
|
|
|
obs_scene_enum_items(scene, select_one, (obs_sceneitem_t*)item);
|
|
}
|
|
|
|
void OBSBasicPreview::DoCtrlSelect(const vec2 &pos)
|
|
{
|
|
OBSSceneItem item = GetItemAtPos(pos, false);
|
|
if (!item)
|
|
return;
|
|
|
|
bool selected = obs_sceneitem_selected(item);
|
|
obs_sceneitem_select(item, !selected);
|
|
}
|
|
|
|
void OBSBasicPreview::ProcessClick(const vec2 &pos)
|
|
{
|
|
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
|
|
|
|
if (modifiers & Qt::ControlModifier)
|
|
DoCtrlSelect(pos);
|
|
else
|
|
DoSelect(pos);
|
|
}
|
|
|
|
void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
if (mouseDown) {
|
|
vec2 pos = GetMouseEventPos(event);
|
|
|
|
if (!mouseMoved)
|
|
ProcessClick(pos);
|
|
|
|
stretchItem = nullptr;
|
|
mouseDown = false;
|
|
mouseMoved = false;
|
|
}
|
|
}
|
|
|
|
struct SelectedItemBounds {
|
|
bool first = true;
|
|
vec3 tl, br;
|
|
};
|
|
|
|
static bool AddItemBounds(obs_scene_t *scene, obs_sceneitem_t *item,
|
|
void *param)
|
|
{
|
|
SelectedItemBounds *data = reinterpret_cast<SelectedItemBounds*>(param);
|
|
|
|
if (!obs_sceneitem_selected(item))
|
|
return true;
|
|
|
|
matrix4 boxTransform;
|
|
obs_sceneitem_get_box_transform(item, &boxTransform);
|
|
|
|
vec3 t[4] = {
|
|
GetTransformedPos(0.0f, 0.0f, boxTransform),
|
|
GetTransformedPos(1.0f, 0.0f, boxTransform),
|
|
GetTransformedPos(0.0f, 1.0f, boxTransform),
|
|
GetTransformedPos(1.0f, 1.0f, boxTransform)
|
|
};
|
|
|
|
for (const vec3 &v : t) {
|
|
if (data->first) {
|
|
vec3_copy(&data->tl, &v);
|
|
vec3_copy(&data->br, &v);
|
|
data->first = false;
|
|
} else {
|
|
vec3_min(&data->tl, &data->tl, &v);
|
|
vec3_max(&data->br, &data->br, &v);
|
|
}
|
|
}
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
void OBSBasicPreview::SnapItemMovement(vec2 &offset)
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
OBSScene scene = main->GetCurrentScene();
|
|
|
|
SelectedItemBounds data;
|
|
obs_scene_enum_items(scene, AddItemBounds, &data);
|
|
|
|
data.tl.x += offset.x;
|
|
data.tl.y += offset.y;
|
|
data.br.x += offset.x;
|
|
data.br.y += offset.y;
|
|
|
|
vec3 snapOffset = GetScreenSnapOffset(data.tl, data.br);
|
|
offset.x += snapOffset.x;
|
|
offset.y += snapOffset.y;
|
|
}
|
|
|
|
static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
|
|
{
|
|
vec2 *offset = reinterpret_cast<vec2*>(param);
|
|
|
|
if (obs_sceneitem_selected(item)) {
|
|
vec2 pos;
|
|
obs_sceneitem_get_pos(item, &pos);
|
|
vec2_add(&pos, &pos, offset);
|
|
obs_sceneitem_set_pos(item, &pos);
|
|
}
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
return true;
|
|
}
|
|
|
|
void OBSBasicPreview::MoveItems(const vec2 &pos)
|
|
{
|
|
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
OBSScene scene = main->GetCurrentScene();
|
|
|
|
vec2 offset, moveOffset;
|
|
vec2_sub(&offset, &pos, &startPos);
|
|
vec2_sub(&moveOffset, &offset, &lastMoveOffset);
|
|
|
|
if (!(modifiers & Qt::ControlModifier))
|
|
SnapItemMovement(moveOffset);
|
|
|
|
vec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset);
|
|
|
|
obs_scene_enum_items(scene, move_items, &moveOffset);
|
|
}
|
|
|
|
vec3 OBSBasicPreview::CalculateStretchPos(const vec3 &tl, const vec3 &br)
|
|
{
|
|
uint32_t alignment = obs_sceneitem_get_alignment(stretchItem);
|
|
vec3 pos;
|
|
|
|
vec3_zero(&pos);
|
|
|
|
if (alignment & OBS_ALIGN_LEFT)
|
|
pos.x = tl.x;
|
|
else if (alignment & OBS_ALIGN_RIGHT)
|
|
pos.x = br.x;
|
|
else
|
|
pos.x = (br.x - tl.x) * 0.5f + tl.x;
|
|
|
|
if (alignment & OBS_ALIGN_TOP)
|
|
pos.y = tl.y;
|
|
else if (alignment & OBS_ALIGN_BOTTOM)
|
|
pos.y = br.y;
|
|
else
|
|
pos.y = (br.y - tl.y) * 0.5f + tl.y;
|
|
|
|
return pos;
|
|
}
|
|
|
|
void OBSBasicPreview::ClampAspect(vec3 &tl, vec3 &br, vec2 &size,
|
|
const vec2 &baseSize)
|
|
{
|
|
float baseAspect = baseSize.x / baseSize.y;
|
|
float aspect = size.x / size.y;
|
|
uint32_t stretchFlags = (uint32_t)stretchHandle;
|
|
|
|
if (stretchHandle == ItemHandle::TopLeft ||
|
|
stretchHandle == ItemHandle::TopRight ||
|
|
stretchHandle == ItemHandle::BottomLeft ||
|
|
stretchHandle == ItemHandle::BottomRight) {
|
|
if (aspect < baseAspect)
|
|
size.x = size.y * baseAspect;
|
|
else
|
|
size.y = size.x / baseAspect;
|
|
|
|
} else if (stretchHandle == ItemHandle::TopCenter ||
|
|
stretchHandle == ItemHandle::BottomCenter) {
|
|
size.x = size.y * baseAspect;
|
|
|
|
} else if (stretchHandle == ItemHandle::CenterLeft ||
|
|
stretchHandle == ItemHandle::CenterRight) {
|
|
size.y = size.x / baseAspect;
|
|
}
|
|
|
|
size.x = std::round(size.x);
|
|
size.y = std::round(size.y);
|
|
|
|
if (stretchFlags & ITEM_LEFT)
|
|
tl.x = br.x - size.x;
|
|
else if (stretchFlags & ITEM_RIGHT)
|
|
br.x = tl.x + size.x;
|
|
|
|
if (stretchFlags & ITEM_TOP)
|
|
tl.y = br.y - size.y;
|
|
else if (stretchFlags & ITEM_BOTTOM)
|
|
br.y = tl.y + size.y;
|
|
}
|
|
|
|
void OBSBasicPreview::SnapStretchingToScreen(vec3 &tl, vec3 &br)
|
|
{
|
|
uint32_t stretchFlags = (uint32_t)stretchHandle;
|
|
vec3 newTL = GetTransformedPos(tl.x, tl.y, itemToScreen);
|
|
vec3 newTR = GetTransformedPos(br.x, tl.y, itemToScreen);
|
|
vec3 newBL = GetTransformedPos(tl.x, br.y, itemToScreen);
|
|
vec3 newBR = GetTransformedPos(br.x, br.y, itemToScreen);
|
|
vec3 boundingTL;
|
|
vec3 boundingBR;
|
|
|
|
vec3_copy(&boundingTL, &newTL);
|
|
vec3_min(&boundingTL, &boundingTL, &newTR);
|
|
vec3_min(&boundingTL, &boundingTL, &newBL);
|
|
vec3_min(&boundingTL, &boundingTL, &newBR);
|
|
|
|
vec3_copy(&boundingBR, &newTL);
|
|
vec3_max(&boundingBR, &boundingBR, &newTR);
|
|
vec3_max(&boundingBR, &boundingBR, &newBL);
|
|
vec3_max(&boundingBR, &boundingBR, &newBR);
|
|
|
|
vec3 offset = GetScreenSnapOffset(boundingTL, boundingBR);
|
|
vec3_add(&offset, &offset, &newTL);
|
|
vec3_transform(&offset, &offset, &screenToItem);
|
|
vec3_sub(&offset, &offset, &tl);
|
|
|
|
if (stretchFlags & ITEM_LEFT)
|
|
tl.x += offset.x;
|
|
else if (stretchFlags & ITEM_RIGHT)
|
|
br.x += offset.x;
|
|
|
|
if (stretchFlags & ITEM_TOP)
|
|
tl.y += offset.y;
|
|
else if (stretchFlags & ITEM_BOTTOM)
|
|
br.y += offset.y;
|
|
}
|
|
|
|
void OBSBasicPreview::StretchItem(const vec2 &pos)
|
|
{
|
|
Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
|
|
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
|
|
uint32_t stretchFlags = (uint32_t)stretchHandle;
|
|
bool shiftDown = (modifiers & Qt::ShiftModifier);
|
|
vec3 tl, br, pos3;
|
|
|
|
vec3_zero(&tl);
|
|
vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
|
|
|
|
vec3_set(&pos3, pos.x, pos.y, 0.0f);
|
|
vec3_transform(&pos3, &pos3, &screenToItem);
|
|
|
|
if (stretchFlags & ITEM_LEFT)
|
|
tl.x = pos3.x;
|
|
else if (stretchFlags & ITEM_RIGHT)
|
|
br.x = pos3.x;
|
|
|
|
if (stretchFlags & ITEM_TOP)
|
|
tl.y = pos3.y;
|
|
else if (stretchFlags & ITEM_BOTTOM)
|
|
br.y = pos3.y;
|
|
|
|
if (!(modifiers & Qt::ControlModifier))
|
|
SnapStretchingToScreen(tl, br);
|
|
|
|
obs_source_t *source = obs_sceneitem_get_source(stretchItem);
|
|
|
|
vec2 baseSize;
|
|
vec2_set(&baseSize,
|
|
float(obs_source_get_width(source)),
|
|
float(obs_source_get_height(source)));
|
|
|
|
vec2 size;
|
|
vec2_set(&size,br. x - tl.x, br.y - tl.y);
|
|
|
|
if (boundsType != OBS_BOUNDS_NONE) {
|
|
if (shiftDown)
|
|
ClampAspect(tl, br, size, baseSize);
|
|
|
|
if (tl.x > br.x) std::swap(tl.x, br.x);
|
|
if (tl.y > br.y) std::swap(tl.y, br.y);
|
|
|
|
vec2_abs(&size, &size);
|
|
|
|
obs_sceneitem_set_bounds(stretchItem, &size);
|
|
} else {
|
|
if (!shiftDown)
|
|
ClampAspect(tl, br, size, baseSize);
|
|
|
|
vec2_div(&size, &size, &baseSize);
|
|
obs_sceneitem_set_scale(stretchItem, &size);
|
|
}
|
|
|
|
pos3 = CalculateStretchPos(tl, br);
|
|
vec3_transform(&pos3, &pos3, &itemToScreen);
|
|
|
|
vec2 newPos;
|
|
vec2_set(&newPos, std::round(pos3.x), std::round(pos3.y));
|
|
obs_sceneitem_set_pos(stretchItem, &newPos);
|
|
}
|
|
|
|
void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
if (mouseDown) {
|
|
vec2 pos = GetMouseEventPos(event);
|
|
|
|
if (!mouseMoved && !mouseOverItems &&
|
|
stretchHandle == ItemHandle::None) {
|
|
ProcessClick(startPos);
|
|
mouseOverItems = SelectedAtPos(startPos);
|
|
}
|
|
|
|
pos.x = std::round(pos.x);
|
|
pos.y = std::round(pos.y);
|
|
|
|
if (stretchHandle != ItemHandle::None)
|
|
StretchItem(pos);
|
|
else if (mouseOverItems)
|
|
MoveItems(pos);
|
|
|
|
mouseMoved = true;
|
|
}
|
|
}
|
|
|
|
static void DrawCircleAtPos(float x, float y, matrix4 &matrix,
|
|
float previewScale)
|
|
{
|
|
struct vec3 pos;
|
|
vec3_set(&pos, x, y, 0.0f);
|
|
vec3_transform(&pos, &pos, &matrix);
|
|
vec3_mulf(&pos, &pos, previewScale);
|
|
|
|
gs_matrix_push();
|
|
gs_matrix_translate(&pos);
|
|
gs_matrix_scale3f(HANDLE_RADIUS, HANDLE_RADIUS, 1.0f);
|
|
gs_draw(GS_LINESTRIP, 0, 0);
|
|
gs_matrix_pop();
|
|
}
|
|
|
|
bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
|
|
obs_sceneitem_t *item, void *param)
|
|
{
|
|
if (!obs_sceneitem_selected(item))
|
|
return true;
|
|
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
gs_load_vertexbuffer(main->circle);
|
|
|
|
matrix4 boxTransform;
|
|
obs_sceneitem_get_box_transform(item, &boxTransform);
|
|
|
|
DrawCircleAtPos(0.0f, 0.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(0.0f, 1.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(1.0f, 0.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(1.0f, 1.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(0.5f, 0.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(0.0f, 0.5f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(0.5f, 1.0f, boxTransform, main->previewScale);
|
|
DrawCircleAtPos(1.0f, 0.5f, boxTransform, main->previewScale);
|
|
|
|
gs_load_vertexbuffer(main->box);
|
|
|
|
gs_matrix_push();
|
|
gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
|
|
gs_matrix_mul(&boxTransform);
|
|
gs_draw(GS_LINESTRIP, 0, 0);
|
|
|
|
gs_matrix_pop();
|
|
|
|
UNUSED_PARAMETER(scene);
|
|
UNUSED_PARAMETER(param);
|
|
return true;
|
|
}
|
|
|
|
void OBSBasicPreview::DrawSceneEditing()
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
|
|
|
gs_effect_t *solid = obs_get_solid_effect();
|
|
gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
|
|
|
|
vec4 color;
|
|
vec4_set(&color, 1.0f, 0.0f, 0.0f, 1.0f);
|
|
gs_effect_set_vec4(gs_effect_get_param_by_name(solid, "color"), &color);
|
|
|
|
gs_technique_begin(tech);
|
|
gs_technique_begin_pass(tech, 0);
|
|
|
|
OBSScene scene = main->GetCurrentScene();
|
|
if (scene)
|
|
obs_scene_enum_items(scene, DrawSelectedItem, this);
|
|
|
|
gs_load_vertexbuffer(nullptr);
|
|
|
|
gs_technique_end_pass(tech);
|
|
gs_technique_end(tech);
|
|
}
|