Implement complex PiP movement.

This commit is contained in:
John Preston 2020-01-07 14:41:42 +03:00
parent a73520c9d8
commit 7a6052db81
1 changed files with 72 additions and 32 deletions

View File

@ -61,48 +61,62 @@ constexpr auto kPipLoaderPriority = 2;
return inner.topLeft() + QPoint(shiftx, shifty); return inner.topLeft() + QPoint(shiftx, shifty);
} }
[[nodiscard]] QRect Transformed(QRect original, QPoint delta, RectPart by) { [[nodiscard]] QRect Transformed(
const auto min = st::pipMinimalSize; QRect original,
QSize minimalSize,
QSize maximalSize,
QPoint delta,
RectPart by) {
const auto width = original.width(); const auto width = original.width();
const auto height = original.height(); const auto height = original.height();
const auto maxx = width - min; const auto x1 = width - minimalSize.width();
const auto maxy = height - min; const auto x2 = maximalSize.width() - width;
const auto y1 = height - minimalSize.height();
const auto y2 = maximalSize.height() - height;
switch (by) { switch (by) {
case RectPart::Center: return original.translated(delta); case RectPart::Center: return original.translated(delta);
case RectPart::TopLeft: case RectPart::TopLeft:
original.setTop(original.y() + std::min(delta.y(), maxy)); original.setTop(original.y() + std::clamp(delta.y(), -y2, y1));
original.setLeft(original.x() + std::min(delta.x(), maxx)); original.setLeft(original.x() + std::clamp(delta.x(), -x2, x1));
return original; return original;
case RectPart::TopRight: case RectPart::TopRight:
original.setTop(original.y() + std::min(delta.y(), maxy)); original.setTop(original.y() + std::clamp(delta.y(), -y2, y1));
original.setWidth(original.width() + std::max(delta.x(), -maxx)); original.setWidth(original.width() + std::clamp(delta.x(), -x1, x2));
return original; return original;
case RectPart::BottomRight: case RectPart::BottomRight:
original.setHeight(original.height() + std::max(delta.y(), -maxy)); original.setHeight(
original.setWidth(original.width() + std::max(delta.x(), -maxx)); original.height() + std::clamp(delta.y(), -y1, y2));
original.setWidth(original.width() + std::clamp(delta.x(), -x1, x2));
return original; return original;
case RectPart::BottomLeft: case RectPart::BottomLeft:
original.setHeight(original.height() + std::max(delta.y(), -maxy)); original.setHeight(
original.setLeft(original.x() + std::min(delta.x(), maxx)); original.height() + std::clamp(delta.y(), -y1, y2));
original.setLeft(original.x() + std::clamp(delta.x(), -x2, x1));
return original; return original;
case RectPart::Left: case RectPart::Left:
original.setLeft(original.x() + std::min(delta.x(), maxx)); original.setLeft(original.x() + std::clamp(delta.x(), -x2, x1));
return original; return original;
case RectPart::Top: case RectPart::Top:
original.setTop(original.y() + std::min(delta.y(), maxy)); original.setTop(original.y() + std::clamp(delta.y(), -y2, y1));
return original; return original;
case RectPart::Right: case RectPart::Right:
original.setWidth(original.width() + std::max(delta.x(), -maxx)); original.setWidth(original.width() + std::clamp(delta.x(), -x1, x2));
return original; return original;
case RectPart::Bottom: case RectPart::Bottom:
original.setHeight(original.height() + std::max(delta.y(), -maxy)); original.setHeight(
original.height() + std::clamp(delta.y(), -y1, y2));
return original; return original;
} }
return original; return original;
Unexpected("RectPart in PiP Transformed."); Unexpected("RectPart in PiP Transformed.");
} }
[[nodiscard]] QRect Constrained(QRect original, QSize ratio, RectPart by) { [[nodiscard]] QRect Constrained(
QRect original,
QSize minimalSize,
QSize maximalSize,
QSize ratio,
RectPart by) {
if (by == RectPart::Center) { if (by == RectPart::Center) {
return original; return original;
} else if (!original.width() && !original.height()) { } else if (!original.width() && !original.height()) {
@ -110,7 +124,7 @@ constexpr auto kPipLoaderPriority = 2;
} }
const auto widthLarger = (original.width() * ratio.height()) const auto widthLarger = (original.width() * ratio.height())
> (original.height() * ratio.width()); > (original.height() * ratio.width());
const auto newSize = ratio.scaled( const auto desiredSize = ratio.scaled(
original.size(), original.size(),
(((RectParts(by) & RectPart::AllCorners) (((RectParts(by) & RectPart::AllCorners)
|| ((by == RectPart::Top || by == RectPart::Bottom) || ((by == RectPart::Top || by == RectPart::Bottom)
@ -119,6 +133,15 @@ constexpr auto kPipLoaderPriority = 2;
&& !widthLarger)) && !widthLarger))
? Qt::KeepAspectRatio ? Qt::KeepAspectRatio
: Qt::KeepAspectRatioByExpanding)); : Qt::KeepAspectRatioByExpanding));
const auto newSize = QSize(
std::clamp(
desiredSize.width(),
minimalSize.width(),
maximalSize.width()),
std::clamp(
desiredSize.height(),
minimalSize.height(),
maximalSize.height()));
switch (by) { switch (by) {
case RectPart::TopLeft: case RectPart::TopLeft:
return QRect( return QRect(
@ -280,15 +303,15 @@ void PipPanel::setPositionOnScreen(Position position, QRect available) {
: QSize(max * _ratio.width() / _ratio.height(), max); : QSize(max * _ratio.width() / _ratio.height(), max);
// At least one side should not be greater than half of screen size. // At least one side should not be greater than half of screen size.
const auto byHeight = (scaled.width() * screen.height()) const auto byWidth = (scaled.width() * screen.height())
> (scaled.height() * screen.width()); > (scaled.height() * screen.width());
const auto fit = QSize(screen.width() / 2, screen.height() / 2); const auto fit = QSize(screen.width() / 2, screen.height() / 2);
const auto normalized = (byHeight && scaled.height() > fit.height()) const auto normalized = (byWidth && scaled.width() > fit.width())
? QSize(fit.width(), fit.width() * scaled.height() / scaled.width())
: (!byWidth && scaled.height() > fit.height())
? QSize( ? QSize(
fit.height() * scaled.width() / scaled.height(), fit.height() * scaled.width() / scaled.height(),
fit.height()) fit.height())
: (!byHeight && scaled.width() > fit.width())
? QSize(fit.width(), fit.width() * scaled.height() / scaled.width())
: scaled; : scaled;
// Apply minimal size. // Apply minimal size.
@ -339,8 +362,7 @@ void PipPanel::paintEvent(QPaintEvent *e) {
QPainter p(this); QPainter p(this);
auto request = FrameRequest(); auto request = FrameRequest();
request.outer = size(); request.resize = request.outer = size();
request.resize = _ratio.scaled(request.outer, Qt::KeepAspectRatio);
request.corners = RectPart(0) request.corners = RectPart(0)
| ((_attached & (RectPart::Left | RectPart::Top)) | ((_attached & (RectPart::Left | RectPart::Top))
? RectPart(0) ? RectPart(0)
@ -446,7 +468,8 @@ void PipPanel::updatePosition(QPoint point) {
Expects(_dragStartGeometry.has_value()); Expects(_dragStartGeometry.has_value());
Expects(_pressState.has_value()); Expects(_pressState.has_value());
const auto screen = (*_pressState == RectPart::Center) const auto dragPart = *_pressState;
const auto screen = (dragPart == RectPart::Center)
? ScreenFromPosition(point) ? ScreenFromPosition(point)
: myScreen() : myScreen()
? myScreen()->availableGeometry() ? myScreen()->availableGeometry()
@ -454,18 +477,35 @@ void PipPanel::updatePosition(QPoint point) {
if (screen.isEmpty()) { if (screen.isEmpty()) {
return; return;
} }
const auto minimalSize = _ratio.scaled(
st::pipMinimalSize,
st::pipMinimalSize,
Qt::KeepAspectRatioByExpanding);
const auto maximalSize = _ratio.scaled(
screen.width() / 2,
screen.height() / 2,
Qt::KeepAspectRatio);
const auto geometry = Transformed( const auto geometry = Transformed(
*_dragStartGeometry, *_dragStartGeometry,
minimalSize,
maximalSize,
point - _pressPoint, point - _pressPoint,
*_pressState); dragPart);
const auto valid = Constrained(geometry, _ratio, *_pressState); const auto valid = Constrained(
//const auto clamped = ClampToEdges(screen, valid); geometry,
//if (clamped != position) { minimalSize,
// moveAnimated(clamped); maximalSize,
//} else { _ratio,
dragPart);
const auto clamped = (dragPart == RectPart::Center)
? ClampToEdges(screen, valid)
: valid.topLeft();
if (clamped != valid.topLeft()) {
moveAnimated(clamped);
} else {
_positionAnimation.stop(); _positionAnimation.stop();
setGeometry(valid); setGeometry(valid);
//} }
} }
void PipPanel::finishDrag(QPoint point) { void PipPanel::finishDrag(QPoint point) {