UI: Allow zoom with the scroll wheel

Adds the ability to zoom into the preview when fixed scaling mode is
enabled.

(Edit by Jim: Also now saves zoom and scroll position)

Closes jp9000/obs-studio#917
This commit is contained in:
Joseph El-Khouri 2017-05-13 13:13:55 -04:00 committed by jp9000
parent d70c97db3a
commit 8942c18695
3 changed files with 103 additions and 52 deletions

View file

@ -430,8 +430,14 @@ void OBSBasic::Save(const char *file)
savedPreviewProjectorList);
obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked());
obs_data_set_int(saveData, "scaling_mode",
static_cast<uint32_t>(ui->preview->GetScalingMode()));
obs_data_set_bool(saveData, "scaling_enabled",
ui->preview->IsFixedScaling());
obs_data_set_int(saveData, "scaling_level",
ui->preview->GetScalingLevel());
obs_data_set_double(saveData, "scaling_off_x",
ui->preview->GetScrollX());
obs_data_set_double(saveData, "scaling_off_y",
ui->preview->GetScrollY());
if (api) {
obs_data_t *moduleObj = obs_data_create();
@ -771,18 +777,20 @@ retryScene:
ui->preview->SetLocked(previewLocked);
ui->actionLockPreview->setChecked(previewLocked);
ScalingMode previewScaling = static_cast<ScalingMode>(
obs_data_get_int(data, "scaling_mode"));
switch (previewScaling) {
case ScalingMode::Window:
case ScalingMode::Canvas:
case ScalingMode::Output:
break;
default:
previewScaling = ScalingMode::Window;
}
/* ---------------------- */
ui->preview->SetScaling(previewScaling);
bool fixedScaling = obs_data_get_bool(data, "scaling_enabled");
int scalingLevel = (int)obs_data_get_int(data, "scaling_level");
float scrollOffX = (float)obs_data_get_double(data, "scaling_off_x");
float scrollOffY = (float)obs_data_get_double(data, "scaling_off_y");
if (fixedScaling) {
ui->preview->SetScalingLevel(scalingLevel);
ui->preview->SetScrollingOffset(scrollOffX, scrollOffY);
}
ui->preview->SetFixedScaling(fixedScaling);
/* ---------------------- */
if (api) {
obs_data_t *modulesObj = obs_data_get_obj(data, "modules");
@ -1848,13 +1856,22 @@ OBSSceneItem OBSBasic::GetCurrentSceneItem()
void OBSBasic::UpdatePreviewScalingMenu()
{
ScalingMode scalingMode = ui->preview->GetScalingMode();
ui->actionScaleWindow->setChecked(
scalingMode == ScalingMode::Window);
ui->actionScaleCanvas->setChecked(
scalingMode == ScalingMode::Canvas);
bool fixedScaling = ui->preview->IsFixedScaling();
float scalingAmount = ui->preview->GetScalingAmount();
if (!fixedScaling) {
ui->actionScaleWindow->setChecked(true);
ui->actionScaleCanvas->setChecked(false);
ui->actionScaleOutput->setChecked(false);
return;
}
obs_video_info ovi;
obs_get_video_info(&ovi);
ui->actionScaleWindow->setChecked(false);
ui->actionScaleCanvas->setChecked(scalingAmount == 1.0f);
ui->actionScaleOutput->setChecked(
scalingMode == ScalingMode::Output);
scalingAmount == float(ovi.output_width) / float(ovi.base_width));
}
void OBSBasic::UpdateSources(OBSScene scene)
@ -2844,32 +2861,23 @@ void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId,
void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
{
QSize targetSize;
ScalingMode scalingMode;
bool isFixedScaling;
obs_video_info ovi;
/* resize preview panel to fix to the top section of the window */
targetSize = GetPixelSize(ui->preview);
scalingMode = ui->preview->GetScalingMode();
isFixedScaling = ui->preview->IsFixedScaling();
obs_get_video_info(&ovi);
if (scalingMode == ScalingMode::Canvas) {
previewScale = 1.0f;
if (isFixedScaling) {
previewScale = ui->preview->GetScalingAmount();
GetCenterPosFromFixedScale(int(cx), int(cy),
targetSize.width() - PREVIEW_EDGE_SIZE * 2,
targetSize.height() - PREVIEW_EDGE_SIZE * 2,
previewX, previewY, previewScale);
previewX += ui->preview->ScrollX();
previewY += ui->preview->ScrollY();
} else if (scalingMode == ScalingMode::Output) {
previewScale = float(ovi.output_width) / float(ovi.base_width);
GetCenterPosFromFixedScale(int(cx), int(cy),
targetSize.width() - PREVIEW_EDGE_SIZE * 2,
targetSize.height() - PREVIEW_EDGE_SIZE * 2,
previewX, previewY, previewScale);
previewX += ui->preview->ScrollX();
previewY += ui->preview->ScrollY();
previewX += ui->preview->GetScrollX();
previewY += ui->preview->GetScrollY();
} else {
GetScaleAndCenterPos(int(cx), int(cy),
@ -5290,26 +5298,38 @@ void OBSBasic::on_scalingMenu_aboutToShow()
text = text.arg(QString::number(ovi.output_width),
QString::number(ovi.output_height));
action->setText(text);
action->setVisible(!(ovi.output_width == ovi.base_width &&
ovi.output_height == ovi.base_height));
UpdatePreviewScalingMenu();
}
void OBSBasic::on_actionScaleWindow_triggered()
{
ui->preview->SetScaling(ScalingMode::Window);
ui->preview->SetFixedScaling(false);
ui->preview->ResetScrollingOffset();
emit ui->preview->DisplayResized();
}
void OBSBasic::on_actionScaleCanvas_triggered()
{
ui->preview->SetScaling(ScalingMode::Canvas);
ui->preview->SetFixedScaling(true);
ui->preview->SetScalingLevel(0);
emit ui->preview->DisplayResized();
}
void OBSBasic::on_actionScaleOutput_triggered()
{
ui->preview->SetScaling(ScalingMode::Output);
obs_video_info ovi;
obs_get_video_info(&ovi);
ui->preview->SetFixedScaling(true);
float scalingAmount = float(ovi.output_width) / float(ovi.base_width);
// log base ZOOM_SENSITIVITY of x = log(x) / log(ZOOM_SENSITIVITY)
int32_t approxScalingLevel = int32_t(
round(log(scalingAmount) / log(ZOOM_SENSITIVITY)));
ui->preview->SetScalingLevel(approxScalingLevel);
ui->preview->SetScalingAmount(scalingAmount);
emit ui->preview->DisplayResized();
}

View file

@ -382,8 +382,7 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
void OBSBasicPreview::keyPressEvent(QKeyEvent *event)
{
if (GetScalingMode() == ScalingMode::Window ||
event->isAutoRepeat()) {
if (!IsFixedScaling() || event->isAutoRepeat()) {
OBSQTDisplay::keyPressEvent(event);
return;
}
@ -415,9 +414,23 @@ void OBSBasicPreview::keyReleaseEvent(QKeyEvent *event)
OBSQTDisplay::keyReleaseEvent(event);
}
void OBSBasicPreview::wheelEvent(QWheelEvent *event)
{
if (scrollMode && IsFixedScaling()
&& event->orientation() == Qt::Vertical) {
if (event->delta() > 0)
SetScalingLevel(scalingLevel + 1);
else if (event->delta() < 0)
SetScalingLevel(scalingLevel - 1);
emit DisplayResized();
}
OBSQTDisplay::wheelEvent(event);
}
void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
{
if (scrollMode && GetScalingMode() != ScalingMode::Window &&
if (scrollMode && IsFixedScaling() &&
event->button() == Qt::LeftButton) {
setCursor(Qt::ClosedHandCursor);
scrollingFrom.x = event->x();
@ -1205,3 +1218,15 @@ void OBSBasicPreview::ResetScrollingOffset()
{
vec2_zero(&scrollingOffset);
}
void OBSBasicPreview::SetScalingLevel(int32_t newScalingLevelVal) {
float newScalingAmountVal = pow(ZOOM_SENSITIVITY, float(newScalingLevelVal));
scalingLevel = newScalingLevelVal;
SetScalingAmount(newScalingAmountVal);
}
void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal) {
scrollingOffset.x *= newScalingAmountVal / scalingAmount;
scrollingOffset.y *= newScalingAmountVal / scalingAmount;
scalingAmount = newScalingAmountVal;
}

View file

@ -14,6 +14,8 @@ class QMouseEvent;
#define ITEM_TOP (1<<2)
#define ITEM_BOTTOM (1<<3)
#define ZOOM_SENSITIVITY 1.125f
enum class ItemHandle : uint32_t {
None = 0,
TopLeft = ITEM_TOP | ITEM_LEFT,
@ -26,12 +28,6 @@ enum class ItemHandle : uint32_t {
BottomRight = ITEM_BOTTOM | ITEM_RIGHT
};
enum class ScalingMode : uint32_t {
Window = 0,
Canvas = 1,
Output = 2
};
class OBSBasicPreview : public OBSQTDisplay {
Q_OBJECT
@ -41,7 +37,6 @@ private:
vec2 cropSize;
OBSSceneItem stretchItem;
ItemHandle stretchHandle = ItemHandle::None;
ScalingMode scale = ScalingMode::Window;
vec2 stretchItemSize;
matrix4 screenToItem;
matrix4 itemToScreen;
@ -56,6 +51,9 @@ private:
bool cropping = false;
bool locked = false;
bool scrollMode = false;
bool fixedScaling = false;
int32_t scalingLevel = 0;
float scalingAmount = 1.0f;
static vec2 GetMouseEventPos(QMouseEvent *event);
static bool DrawSelectedItem(obs_scene_t *scene, obs_sceneitem_t *item,
@ -88,22 +86,30 @@ public:
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void keyReleaseEvent(QKeyEvent *event) override;
virtual void wheelEvent(QWheelEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
void DrawSceneEditing();
void ResetScrollingOffset();
inline void SetLocked(bool newLockedVal) {locked = newLockedVal;}
inline void ToggleLocked() {locked = !locked;}
inline bool Locked() const {return locked;}
inline void SetScaling(ScalingMode newScaledVal) {scale = newScaledVal;}
inline ScalingMode GetScalingMode() const {return scale;}
inline void SetFixedScaling(bool newFixedScalingVal) { fixedScaling = newFixedScalingVal; }
inline bool IsFixedScaling() const { return fixedScaling; }
inline float ScrollX() const {return scrollingOffset.x;}
inline float ScrollY() const {return scrollingOffset.y;}
void SetScalingLevel(int32_t newScalingLevelVal);
void SetScalingAmount(float newScalingAmountVal);
inline int32_t GetScalingLevel() const { return scalingLevel; }
inline float GetScalingAmount() const { return scalingAmount; }
void ResetScrollingOffset();
inline void SetScrollingOffset(float x, float y) {vec2_set(&scrollingOffset, x, y);}
inline float GetScrollX() const {return scrollingOffset.x;}
inline float GetScrollY() const {return scrollingOffset.y;}
/* use libobs allocator for alignment because the matrices itemToScreen
* and screenToItem may contain SSE data, which will cause SSE