Merge branch 'refs/heads/dev' into plugin
# Conflicts: # .gitmodules # CMakeLists.txt # ext/core
This commit is contained in:
+6
-4
@@ -1,6 +1,8 @@
|
||||
[submodule "ext/KDDockWidgets"]
|
||||
path = ext/KDDockWidgets
|
||||
url = https://github.com/olive-editor/KDDockWidgets.git
|
||||
[submodule "ext/core"]
|
||||
path = ext/core
|
||||
url = https://github.com/olive-editor/core
|
||||
url = https://github.com/OliveCommunity/core.git
|
||||
branch = dev
|
||||
[submodule "ext/KDDockWidgets"]
|
||||
path = ext/KDDockWidgets
|
||||
url = https://github.com/OliveCommunity/KDDockWidgets.git
|
||||
branch = main
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
From 6b0ef44eb189411d36c739ccde8a081a3e62034a Mon Sep 17 00:00:00 2001
|
||||
From: Mike Solar <iam@mikesolar.com>
|
||||
Date: Mon, 24 Nov 2025 20:57:12 +0800
|
||||
Subject: [PATCH] Fix include path for Window_p.h in qtcommon directory
|
||||
|
||||
---
|
||||
src/qtcommon/Window_p.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/qtcommon/Window_p.h b/src/qtcommon/Window_p.h
|
||||
index f29b209a5..d1d6b47ad 100644
|
||||
--- a/src/qtcommon/Window_p.h
|
||||
+++ b/src/qtcommon/Window_p.h
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
-#include "core/Window_p.h"
|
||||
+#include "../core/Window_p.h"
|
||||
#include "Screen_p.h"
|
||||
|
||||
#include <QPointer>
|
||||
--
|
||||
2.52.0
|
||||
|
||||
+9
-11
@@ -14,16 +14,14 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
cmake_minimum_required(VERSION 3.30 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD ON)
|
||||
find_package(expat REQUIRED)
|
||||
|
||||
if(${CMAKE_BUILD_TYPE} EQUAL Debug)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g")
|
||||
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory)
|
||||
endif ()
|
||||
option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON)
|
||||
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
|
||||
option(BUILD_TESTS "Build unit tests" OFF)
|
||||
@@ -37,11 +35,10 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
|
||||
# Generates a compile_commands.json in the build dir, link that to the repo
|
||||
# root to enrich your IDE with clangd language server protocol functionalities
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
add_subdirectory(third_party/openfx/HostSupport)
|
||||
|
||||
# Sanitizers
|
||||
add_library(olive-sanitizers INTERFACE)
|
||||
include(cmake/Sanitizers.cmake)
|
||||
@@ -110,6 +107,7 @@ list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES})
|
||||
list(APPEND OLIVE_LIBRARIES olivecore)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include)
|
||||
|
||||
|
||||
# Link Qt
|
||||
set(QT_LIBRARIES
|
||||
Core
|
||||
@@ -175,7 +173,7 @@ list(APPEND OLIVE_LIBRARIES
|
||||
)
|
||||
|
||||
# Link FFmpeg
|
||||
find_package(FFMPEG 7.0 REQUIRED
|
||||
find_package(FFMPEG REQUIRED
|
||||
COMPONENTS
|
||||
avutil
|
||||
avcodec
|
||||
|
||||
@@ -26,6 +26,7 @@ extern "C" {
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/frame.h>
|
||||
}
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
@@ -137,7 +138,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
|
||||
break;
|
||||
}
|
||||
|
||||
AVFrame *hw_in = f.get();
|
||||
AVFramePtr hw_in = f;
|
||||
|
||||
VideoParams plane_params = vp;
|
||||
plane_params.set_channel_count(1);
|
||||
@@ -145,7 +146,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
|
||||
|
||||
TexturePtr y_plane = p.renderer->CreateTexture(
|
||||
plane_params, hw_in->data[0], hw_in->linesize[0] / px_size);
|
||||
|
||||
y_plane->handleFrame(hw_in);
|
||||
switch (f->format) {
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
case AV_PIX_FMT_YUV422P:
|
||||
@@ -167,8 +168,11 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
|
||||
|
||||
TexturePtr u_plane = p.renderer->CreateTexture(
|
||||
plane_params, hw_in->data[1], hw_in->linesize[1] / px_size);
|
||||
u_plane->handleFrame(hw_in);
|
||||
|
||||
TexturePtr v_plane = p.renderer->CreateTexture(
|
||||
plane_params, hw_in->data[2], hw_in->linesize[2] / px_size);
|
||||
v_plane->handleFrame(hw_in);
|
||||
|
||||
ShaderJob job;
|
||||
job.Insert(QStringLiteral("y_channel"),
|
||||
@@ -204,6 +208,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f,
|
||||
case AV_PIX_FMT_RGBA:
|
||||
case AV_PIX_FMT_RGBA64LE:
|
||||
// RGBA can be uploaded directly to the texture
|
||||
tex->handleFrame(f);
|
||||
tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel());
|
||||
break;
|
||||
}
|
||||
@@ -279,14 +284,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
AVCOL_RANGE_MPEG;
|
||||
|
||||
// Perform any CPU processing required
|
||||
f = PreProcessFrame(f, p);
|
||||
AVFramePtr ptr = PreProcessFrame(f, p);
|
||||
f=std::move(ptr);
|
||||
if (!f) {
|
||||
// Error occurred while software scaling
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Finally, perform any GPU processing required
|
||||
return ProcessFrameIntoTexture(f, p, original);
|
||||
TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -380,116 +388,77 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
|
||||
VideoParams::kInterlaceNone;
|
||||
|
||||
{
|
||||
// Read at least two frames to get more information about this video stream
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
// Read at least two frames to get more information about this video stream
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
|
||||
{
|
||||
Instance instance;
|
||||
instance.Open(filename_c, avstream->index);
|
||||
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
|
||||
AVRational pixel_aspect_ratio = {1, 1};
|
||||
AVRational frame_rate = avstream->avg_frame_rate;
|
||||
AVPixelFormat compatible_pix_fmt =
|
||||
FFmpegUtils::GetCompatiblePixelFormat(
|
||||
static_cast<AVPixelFormat>(avstream->codecpar->format));
|
||||
bool image_is_still = false;
|
||||
|
||||
// Read first frame and retrieve some metadata
|
||||
if (instance.GetFrame(pkt, frame) >= 0) {
|
||||
// Check if video is interlaced and what field dominance it has if so
|
||||
if (frame->interlaced_frame) {
|
||||
if (frame->top_field_first) {
|
||||
interlacing =
|
||||
VideoParams::kInterlacedTopFirst;
|
||||
} else {
|
||||
interlacing =
|
||||
VideoParams::kInterlacedBottomFirst;
|
||||
}
|
||||
}
|
||||
{
|
||||
Instance instance;
|
||||
if (instance.Open(filename_c, avstream->index) != 0)
|
||||
goto cleanup;
|
||||
|
||||
pixel_aspect_ratio =
|
||||
av_guess_sample_aspect_ratio(
|
||||
instance.fmt_ctx(), instance.avstream(),
|
||||
frame);
|
||||
AVCodecContext *avctx = instance.codec_ctx();
|
||||
interlacing = FFmpegFieldOrderToOlive(avctx->field_order);
|
||||
|
||||
frame_rate = av_guess_frame_rate(
|
||||
instance.fmt_ctx(), instance.avstream(),
|
||||
frame);
|
||||
if (instance.GetFrame(pkt, frame) >= 0) {
|
||||
pixel_aspect_ratio =
|
||||
av_guess_sample_aspect_ratio(instance.fmt_ctx(),
|
||||
instance.avstream(), frame);
|
||||
frame_rate =
|
||||
av_guess_frame_rate(instance.fmt_ctx(),
|
||||
instance.avstream(), frame);
|
||||
}
|
||||
|
||||
compatible_pix_fmt =
|
||||
FFmpegUtils::GetCompatiblePixelFormat(
|
||||
static_cast<AVPixelFormat>(
|
||||
avstream->codecpar->format));
|
||||
}
|
||||
int ret = instance.GetFrame(pkt, frame);
|
||||
if (ret == AVERROR_EOF) {
|
||||
image_is_still = true;
|
||||
} else if (avstream->duration == AV_NOPTS_VALUE ||
|
||||
duration_guessed_from_bitrate) {
|
||||
int64_t last_ts = frame->best_effort_timestamp;
|
||||
while (instance.GetFrame(pkt, frame) >= 0 &&
|
||||
(!cancelled || !cancelled->IsCancelled()))
|
||||
last_ts = frame->best_effort_timestamp;
|
||||
avstream->duration = last_ts;
|
||||
}
|
||||
|
||||
// Read second frame
|
||||
int ret = instance.GetFrame(pkt, frame);
|
||||
instance.Close();
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
// Check if we need a manual duration
|
||||
if (avstream->duration == AV_NOPTS_VALUE ||
|
||||
duration_guessed_from_bitrate) {
|
||||
if (footage_duration == AV_NOPTS_VALUE ||
|
||||
duration_guessed_from_bitrate) {
|
||||
// Manually read through file for duration
|
||||
int64_t new_dur;
|
||||
cleanup:
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
do {
|
||||
new_dur =
|
||||
frame->best_effort_timestamp;
|
||||
} while (instance.GetFrame(
|
||||
pkt, frame) >= 0 &&
|
||||
(!cancelled ||
|
||||
!cancelled->IsCancelled()));
|
||||
VideoParams stream;
|
||||
stream.set_stream_index(i);
|
||||
stream.set_width(avstream->codecpar->width);
|
||||
stream.set_height(avstream->codecpar->height);
|
||||
stream.set_video_type(image_is_still ? VideoParams::kVideoTypeStill
|
||||
: VideoParams::kVideoTypeVideo);
|
||||
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
||||
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
|
||||
stream.set_interlacing(interlacing); // <-- 已正确填充
|
||||
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
||||
stream.set_frame_rate(frame_rate);
|
||||
stream.set_start_time(avstream->start_time);
|
||||
stream.set_time_base(avstream->time_base);
|
||||
stream.set_duration(avstream->duration);
|
||||
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG
|
||||
? VideoParams::kColorRangeFull
|
||||
: VideoParams::kColorRangeLimited);
|
||||
stream.set_premultiplied_alpha(false);
|
||||
|
||||
avstream->duration = new_dur;
|
||||
|
||||
} else {
|
||||
// Fallback to footage duration
|
||||
avstream->duration =
|
||||
Timecode::rescale_timestamp_ceil(
|
||||
footage_duration,
|
||||
rational(1, AV_TIME_BASE),
|
||||
avstream->time_base);
|
||||
}
|
||||
}
|
||||
} else if (ret == AVERROR_EOF) {
|
||||
// Video has only one frame in it, treat it like a still image
|
||||
image_is_still = true;
|
||||
}
|
||||
|
||||
instance.Close();
|
||||
}
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
desc.AddVideoStream(stream);
|
||||
image_is_still ? still_streams++ : video_streams++;
|
||||
}
|
||||
|
||||
VideoParams stream;
|
||||
stream.set_stream_index(i);
|
||||
stream.set_width(avstream->codecpar->width);
|
||||
stream.set_height(avstream->codecpar->height);
|
||||
stream.set_video_type((image_is_still) ?
|
||||
VideoParams::kVideoTypeStill :
|
||||
VideoParams::kVideoTypeVideo);
|
||||
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
|
||||
stream.set_channel_count(
|
||||
GetNativeChannelCount(compatible_pix_fmt));
|
||||
stream.set_interlacing(interlacing);
|
||||
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
|
||||
stream.set_frame_rate(frame_rate);
|
||||
stream.set_start_time(avstream->start_time);
|
||||
stream.set_time_base(avstream->time_base);
|
||||
stream.set_duration(avstream->duration);
|
||||
stream.set_color_range(avstream->codecpar->color_range ==
|
||||
AVCOL_RANGE_JPEG ?
|
||||
VideoParams::kColorRangeFull :
|
||||
VideoParams::kColorRangeLimited);
|
||||
|
||||
// Defaults to false, requires user intervention if incorrect
|
||||
stream.set_premultiplied_alpha(false);
|
||||
|
||||
desc.AddVideoStream(stream);
|
||||
|
||||
if (image_is_still) {
|
||||
still_streams++;
|
||||
} else {
|
||||
video_streams++;
|
||||
}
|
||||
|
||||
} else if (avstream->codecpar->codec_type ==
|
||||
AVMEDIA_TYPE_AUDIO) {
|
||||
@@ -627,7 +596,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
|
||||
}
|
||||
// Create resampling context
|
||||
AVChannelLayout layout = params.channel_layout();
|
||||
SwrContext *resampler;
|
||||
SwrContext *resampler=NULL;
|
||||
swr_alloc_set_opts2(
|
||||
&resampler, &layout,
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
|
||||
@@ -835,7 +804,7 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
|
||||
dest->format = f->format;
|
||||
dest->color_range = f->color_range;
|
||||
dest->colorspace = f->colorspace;
|
||||
|
||||
dest->hw_frames_ctx = nullptr;
|
||||
if (p.divider > 1) {
|
||||
dest->width = VideoParams::GetScaledDimension(dest->width, p.divider);
|
||||
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
|
||||
@@ -888,8 +857,8 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f,
|
||||
dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000);
|
||||
}
|
||||
|
||||
r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data,
|
||||
dest->linesize);
|
||||
r = sws_scale_frame(sws_ctx_, dest.get(), f.get());
|
||||
|
||||
if (r < 0) {
|
||||
FFmpegError(r);
|
||||
return nullptr;
|
||||
|
||||
@@ -40,6 +40,20 @@ extern "C" {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
// 头文件里先备好枚举映射
|
||||
static VideoParams::Interlacing FFmpegFieldOrderToOlive(AVFieldOrder fo)
|
||||
{
|
||||
switch (fo) {
|
||||
case AV_FIELD_TT: // 隔行,顶场在前
|
||||
return VideoParams::kInterlacedTopFirst;
|
||||
case AV_FIELD_BB: // 隔行,底场在前
|
||||
return VideoParams::kInterlacedBottomFirst;
|
||||
case AV_FIELD_PROGRESSIVE:
|
||||
default:
|
||||
return VideoParams::kInterlaceNone;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||
*/
|
||||
@@ -122,6 +136,10 @@ private:
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
AVCodecContext *codec_ctx()
|
||||
{
|
||||
return codec_ctx_;
|
||||
}
|
||||
|
||||
private:
|
||||
AVFormatContext *fmt_ctx_;
|
||||
|
||||
@@ -22,6 +22,7 @@ extern "C" {
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include <QFile>
|
||||
@@ -866,7 +867,7 @@ void FFmpegEncoder::FlushEncoders()
|
||||
}
|
||||
|
||||
if (fmt_ctx_) {
|
||||
if (fmt_ctx_->oformat->flags & AVFMT_ALLOW_FLUSH) {
|
||||
if (fmt_ctx_->oformat->flags) {
|
||||
int r = av_interleaved_write_frame(fmt_ctx_, nullptr);
|
||||
if (r < 0) {
|
||||
FFmpegError(tr("Failed to write interleaved packet"), r);
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||
{
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
|
||||
}
|
||||
inline AVFramePtr CreateAVFramePtr()
|
||||
{
|
||||
|
||||
@@ -774,6 +774,7 @@ void Core::StartGUI(bool full_screen)
|
||||
connect(qApp, &QApplication::focusChanged, PanelManager::instance(),
|
||||
&PanelManager::FocusChanged);
|
||||
|
||||
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
|
||||
// Set KDDockWidgets flags
|
||||
auto &config = KDDockWidgets::Config::self();
|
||||
auto flags = config.flags();
|
||||
|
||||
@@ -143,6 +143,7 @@ int decompress_project(const QString &project)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
// Set up debug handler
|
||||
qInstallMessageHandler(olive::DebugHandler);
|
||||
|
||||
|
||||
+15
-3
@@ -25,23 +25,26 @@
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QVariant>
|
||||
#include "Window_p.h"
|
||||
|
||||
#include "panel/panelmanager.h"
|
||||
|
||||
#include <OpenImageIO/hash.h>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super KDDockWidgets::DockWidget
|
||||
#define super KDDockWidgets::QtWidgets::DockWidget
|
||||
|
||||
PanelWidget::PanelWidget(const QString &object_name)
|
||||
: super(object_name)
|
||||
, border_visible_(false)
|
||||
, signal_instead_of_close_(false)
|
||||
{
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
View<QWidget>::setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
connect(this, &PanelWidget::shown, this,
|
||||
static_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
|
||||
reinterpret_cast<void (PanelWidget::*)()>(&PanelWidget::setFocus));
|
||||
|
||||
PanelManager::instance()->RegisterPanel(this);
|
||||
}
|
||||
@@ -123,6 +126,15 @@ void PanelWidget::changeEvent(QEvent *e)
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
if (e->type() == QEvent::WindowStateChange) {
|
||||
if (isVisible() && !isMinimized()) {
|
||||
emit shown(Qt::OtherFocusReason);
|
||||
}
|
||||
else {
|
||||
emit hidden();
|
||||
}
|
||||
}
|
||||
super::changeEvent(e);
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -19,18 +19,23 @@
|
||||
#ifndef PANEL_WIDGET_H
|
||||
#define PANEL_WIDGET_H
|
||||
|
||||
#include "KDDockWidgets/src/core/Window_p.h"
|
||||
#include "KDDockWidgets/src/qtwidgets/views/TabBar.h"
|
||||
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
#include <QEvent>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A widget that is always dockable within the MainWindow.
|
||||
*/
|
||||
class PanelWidget : public KDDockWidgets::DockWidget {
|
||||
class PanelWidget : public KDDockWidgets::QtWidgets::DockWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -277,7 +282,8 @@ public:
|
||||
|
||||
signals:
|
||||
void CloseRequested();
|
||||
|
||||
void shown(Qt::FocusReason reason);
|
||||
void hidden();
|
||||
protected:
|
||||
/**
|
||||
* @brief paintEvent
|
||||
@@ -321,7 +327,7 @@ protected slots:
|
||||
* String to set the subtitle to
|
||||
*/
|
||||
void SetSubtitle(const QString &t);
|
||||
|
||||
protected slots:
|
||||
private:
|
||||
/**
|
||||
* @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change.
|
||||
@@ -337,6 +343,10 @@ private:
|
||||
bool border_visible_;
|
||||
|
||||
bool signal_instead_of_close_;
|
||||
|
||||
QMetaObject::Connection m_tabBarConnection;
|
||||
QMetaObject::Connection m_windowConnection;
|
||||
bool m_lastVisibleState = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ void ProjectPanel::ItemDoubleClickSlot(Node *item)
|
||||
PanelManager::instance()->MostRecentlyFocused<FootageViewerPanel>();
|
||||
panel->ConnectViewerNode(static_cast<Footage *>(item));
|
||||
panel->raise();
|
||||
panel->setFocus();
|
||||
panel->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
} else if (dynamic_cast<Sequence *>(item)) {
|
||||
// Open this sequence in the Timeline
|
||||
Core::instance()->main_window()->OpenSequence(
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#ifndef RENDERTEXTURE_H
|
||||
#define RENDERTEXTURE_H
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <QVariant>
|
||||
|
||||
@@ -148,7 +150,10 @@ public:
|
||||
{
|
||||
return job_;
|
||||
}
|
||||
|
||||
void handleFrame(AVFramePtr ptr)
|
||||
{
|
||||
frame_=ptr;
|
||||
}
|
||||
private:
|
||||
Renderer *renderer_;
|
||||
|
||||
@@ -157,6 +162,8 @@ private:
|
||||
QVariant id_;
|
||||
|
||||
AcceleratedJob *job_;
|
||||
|
||||
AVFramePtr frame_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ const double &TimeScaledObject::timebase_dbl() const
|
||||
rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale,
|
||||
const rational &timebase, bool round)
|
||||
{
|
||||
if (timebase.isNull()) {
|
||||
return rational();
|
||||
}
|
||||
double unscaled_time = x / x_scale / timebase.toDouble();
|
||||
|
||||
// Adjust screen point by scale and timebase
|
||||
@@ -85,16 +88,25 @@ rational TimeScaledObject::SceneToTimeNoGrid(const double &x,
|
||||
|
||||
double TimeScaledObject::TimeToScene(const rational &time) const
|
||||
{
|
||||
if (timebase_.isNull()) {
|
||||
return 0.0;
|
||||
}
|
||||
return time.toDouble() * scale_;
|
||||
}
|
||||
|
||||
rational TimeScaledObject::SceneToTime(const double &x, bool round) const
|
||||
{
|
||||
if (timebase_.isNull()) {
|
||||
return rational();
|
||||
}
|
||||
return SceneToTime(x, scale_, timebase_, round);
|
||||
}
|
||||
|
||||
rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const
|
||||
{
|
||||
if (timebase_.isNull()) {
|
||||
return rational::fromDouble(x / scale_);
|
||||
}
|
||||
return SceneToTimeNoGrid(x, scale_);
|
||||
}
|
||||
|
||||
|
||||
@@ -1201,8 +1201,11 @@ void ViewerWidget::UpdateMinimumScale()
|
||||
// Avoids divide by zero
|
||||
SetMinimumScale(0);
|
||||
} else {
|
||||
SetMinimumScale(static_cast<double>(ruler()->width()) /
|
||||
GetConnectedNode()->GetLength().toDouble());
|
||||
double min_scale = static_cast<double>(ruler()->width()) /
|
||||
GetConnectedNode()->GetLength().toDouble();
|
||||
// Ensure min_scale doesn't exceed max_scale to prevent crash
|
||||
min_scale = qMin(min_scale, GetMaximumScale());
|
||||
SetMinimumScale(min_scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ bool ViewerDisplayWidget::eventFilter(QObject *o, QEvent *e)
|
||||
void ViewerDisplayWidget::OnPaint()
|
||||
{
|
||||
// Clear background to empty
|
||||
QColor bg_color = show_widget_background_ ? palette().window().color() :
|
||||
QColor bg_color = show_widget_background_ ? palette().window().color() :
|
||||
Qt::black;
|
||||
renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(),
|
||||
bg_color.blueF());
|
||||
|
||||
@@ -27,15 +27,17 @@
|
||||
#include <QOffscreenSurface>
|
||||
#endif
|
||||
|
||||
#include "KDDockWidgets/src/qtwidgets/Window_p.h"
|
||||
#include "dialog/about/about.h"
|
||||
#include "mainmenu.h"
|
||||
#include "mainstatusbar.h"
|
||||
#include "KDDockWidgets/src/LayoutSaver.h"
|
||||
#include "timeline/timelineundoworkarea.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super KDDockWidgets::MainWindow
|
||||
#define super KDDockWidgets::QtWidgets::MainWindow
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None,
|
||||
@@ -93,7 +95,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
// emit the "shown" signal before emitting the "hidden" signals, resulting
|
||||
// in Core thinking there are -1 pixel samplers open. To mitigate that,
|
||||
// we force "shown" to emit ourselves here.
|
||||
emit pixel_sampler_panel_->shown();
|
||||
emit pixel_sampler_panel_->shown(Qt::OtherFocusReason);
|
||||
|
||||
// Make node-related connections
|
||||
connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts,
|
||||
@@ -380,7 +382,7 @@ void MainWindow::ToggleMaximizedPanel()
|
||||
premaximized_state_.clear();
|
||||
|
||||
currently_focused_panel->raise();
|
||||
currently_focused_panel->setFocus();
|
||||
currently_focused_panel->setFocus(Qt::ActiveWindowFocusReason);
|
||||
|
||||
PanelManager::instance()->SetSuppressChangedSignal(false);
|
||||
}
|
||||
@@ -428,7 +430,7 @@ void MainWindow::SetProject(Project *p)
|
||||
project_panel_->set_project(p);
|
||||
|
||||
if (project_) {
|
||||
project_panel_->setFocus();
|
||||
project_panel_->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace olive
|
||||
/**
|
||||
* @brief Olive's main window responsible for docking widgets and the main menu bar.
|
||||
*/
|
||||
class MainWindow : public KDDockWidgets::MainWindow {
|
||||
class MainWindow : public KDDockWidgets::QtWidgets::MainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWindow(QWidget *parent = nullptr);
|
||||
|
||||
+1
-1
Submodule ext/KDDockWidgets updated: 8d2d0a5764...0e9723b96b
+1
-1
Submodule ext/core updated: 08eeee5fa9...50caae473d
Reference in New Issue
Block a user