Merge branch 'master' into cache-update
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
#define AUDIOMONITORWIDGET_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QTimer>
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AudioMonitor : public QOpenGLWindow
|
||||
class AudioMonitor : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -741,7 +741,7 @@ void TimeBasedWidget::GoToOut()
|
||||
|
||||
void TimeBasedWidget::DeleteSelected()
|
||||
{
|
||||
if (ruler_->underMouse()) {
|
||||
if (ruler_->HasItemsSelected()) {
|
||||
ruler_->DeleteSelected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &blocks,
|
||||
|
||||
void TimelineWidget::DeleteSelected(bool ripple)
|
||||
{
|
||||
if (ruler()->hasFocus()) {
|
||||
if (ruler()->HasItemsSelected()) {
|
||||
ruler()->DeleteSelected();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ public:
|
||||
|
||||
void SeekToScenePoint(qreal scene);
|
||||
|
||||
bool HasItemsSelected() const
|
||||
{
|
||||
return !selection_manager_.GetSelectedObjects().empty();
|
||||
}
|
||||
|
||||
const std::vector<TimelineMarker*> &GetSelectedMarkers() const
|
||||
{
|
||||
return selection_manager_.GetSelectedObjects();
|
||||
|
||||
@@ -60,6 +60,7 @@ void FootageViewerWidget::ConnectNodeEvent(ViewerOutput *n)
|
||||
{
|
||||
super::ConnectNodeEvent(n);
|
||||
|
||||
IgnoreNextScrubEvent();
|
||||
SetTime(cached_timestamps_.value(n, 0));
|
||||
}
|
||||
|
||||
@@ -70,6 +71,7 @@ void FootageViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
|
||||
super::DisconnectNodeEvent(n);
|
||||
|
||||
IgnoreNextScrubEvent();
|
||||
SetTime(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
recording_(false),
|
||||
first_requeue_watcher_(nullptr),
|
||||
enable_audio_scrubbing_(true),
|
||||
waveform_mode_(kWFAutomatic)
|
||||
waveform_mode_(kWFAutomatic),
|
||||
ignore_scrub_(0)
|
||||
{
|
||||
// Set up main layout
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
@@ -85,7 +86,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
layout->addWidget(sizer_);
|
||||
|
||||
display_widget_ = new ViewerDisplayWidget();
|
||||
display_widget_->setAcceptDrops(true);
|
||||
display_widget_->SetShowWidgetBackground(true);
|
||||
playback_devices_.append(display_widget_);
|
||||
connect(display_widget_, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
@@ -147,8 +147,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
|
||||
instances_.append(this);
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
auto_cacher_ = new PreviewAutoCacher(this);
|
||||
connect(display_widget_, &ViewerDisplayWidget::ColorProcessorChanged, auto_cacher_, &PreviewAutoCacher::SetDisplayColorProcessor);
|
||||
|
||||
@@ -260,6 +258,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateWaveformViewFromMode);
|
||||
|
||||
CloseAudioProcessor();
|
||||
audio_scrub_watchers_.clear();
|
||||
|
||||
SetDisplayImage(QVariant());
|
||||
|
||||
@@ -485,7 +484,7 @@ void ViewerWidget::DisarmRecording()
|
||||
void ViewerWidget::UpdateAudioProcessor()
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
audio_processor_.Close();
|
||||
CloseAudioProcessor();
|
||||
|
||||
AudioParams ap = GetConnectedNode()->GetAudioParams();
|
||||
AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(),
|
||||
@@ -679,30 +678,33 @@ void ViewerWidget::ReceivedAudioBufferForPlayback()
|
||||
|
||||
void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
||||
{
|
||||
// NOTE: Might be good to organize a queue for this in the event that audio takes a long time to
|
||||
// keep the scrubbed chunks ordered, similar to the playback_queue_ or audio_playback_queue_
|
||||
|
||||
RenderTicketWatcher *watcher = static_cast<RenderTicketWatcher *>(sender());
|
||||
|
||||
if (watcher->HasResult()) {
|
||||
SampleBuffer samples = watcher->Get().value<SampleBuffer>();
|
||||
if (samples.is_allocated()) {
|
||||
if (samples.audio_params().channel_count() > 0) {
|
||||
AudioProcessor::Buffer buf;
|
||||
int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf);
|
||||
while (!audio_scrub_watchers_.empty() && audio_scrub_watchers_.front() != watcher) {
|
||||
audio_scrub_watchers_.pop_front();
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
if (!buf.empty()) {
|
||||
QString error;
|
||||
const QByteArray &packed = buf.at(0);
|
||||
AudioManager::instance()->ClearBufferedOutput();
|
||||
if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), packed, &error)) {
|
||||
Core::instance()->ShowStatusBarMessage(tr("Audio scrubbing failed: %1").arg(error));
|
||||
if (!audio_scrub_watchers_.empty()) {
|
||||
if (watcher->HasResult()) {
|
||||
SampleBuffer samples = watcher->Get().value<SampleBuffer>();
|
||||
if (samples.is_allocated()) {
|
||||
if (samples.audio_params().channel_count() > 0) {
|
||||
AudioProcessor::Buffer buf;
|
||||
int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf);
|
||||
|
||||
if (r >= 0) {
|
||||
if (!buf.empty()) {
|
||||
QString error;
|
||||
const QByteArray &packed = buf.at(0);
|
||||
AudioManager::instance()->ClearBufferedOutput();
|
||||
if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), packed, &error)) {
|
||||
Core::instance()->ShowStatusBarMessage(tr("Audio scrubbing failed: %1").arg(error));
|
||||
}
|
||||
AudioMonitor::PushSampleBufferOnAll(samples);
|
||||
}
|
||||
AudioMonitor::PushSampleBufferOnAll(samples);
|
||||
} else {
|
||||
qCritical() << "Failed to process audio for scrubbing:" << r;
|
||||
}
|
||||
} else {
|
||||
qCritical() << "Failed to process audio for scrubbing:" << r;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -932,16 +934,23 @@ void ViewerWidget::PauseInternal()
|
||||
void ViewerWidget::PushScrubbedAudio()
|
||||
{
|
||||
if (!IsPlaying() && GetConnectedNode() && OLIVE_CONFIG("AudioScrubbing").toBool() && enable_audio_scrubbing_) {
|
||||
// Get audio src device from renderer
|
||||
const AudioParams& params = GetConnectedNode()->GetAudioParams();
|
||||
if (ignore_scrub_ > 0) {
|
||||
ignore_scrub_--;
|
||||
}
|
||||
|
||||
if (params.is_valid()) {
|
||||
// NOTE: Hardcoded scrubbing interval (20ms)
|
||||
rational interval = rational(20, 1000);
|
||||
if (ignore_scrub_ == 0) {
|
||||
// Get audio src device from renderer
|
||||
const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters();
|
||||
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing);
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval)));
|
||||
if (params.is_valid()) {
|
||||
// NOTE: Hardcoded scrubbing interval (20ms)
|
||||
rational interval = rational(20, 1000);
|
||||
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing);
|
||||
audio_scrub_watchers_.push_back(watcher);
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,11 @@ protected:
|
||||
return display_widget_;
|
||||
}
|
||||
|
||||
void IgnoreNextScrubEvent()
|
||||
{
|
||||
ignore_scrub_++;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t GetTimestamp() const
|
||||
{
|
||||
@@ -279,6 +284,8 @@ private:
|
||||
|
||||
static QVector<ViewerWidget*> instances_;
|
||||
|
||||
std::list<RenderTicketWatcher*> audio_scrub_watchers_;
|
||||
|
||||
bool record_armed_;
|
||||
bool recording_;
|
||||
TimelineWidget *recording_callback_;
|
||||
@@ -295,6 +302,8 @@ private:
|
||||
|
||||
QVector<RenderTicketWatcher*> dry_run_watchers_;
|
||||
|
||||
int ignore_scrub_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
|
||||
+343
-116
@@ -44,7 +44,7 @@
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/gizmo/screen.h"
|
||||
#include "viewertexteditor.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -67,7 +67,8 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
playback_speed_(0),
|
||||
push_mode_(kPushNull),
|
||||
add_band_(false),
|
||||
queue_starved_(false)
|
||||
queue_starved_(false),
|
||||
text_edit_(nullptr)
|
||||
{
|
||||
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::ToolChanged);
|
||||
|
||||
@@ -76,6 +77,8 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
|
||||
const int kFrameRateAverageCount = 8;
|
||||
frame_rate_averages_.resize(kFrameRateAverageCount);
|
||||
|
||||
inner_widget()->setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetMatrixTranslate(const QMatrix4x4 &mat)
|
||||
@@ -240,48 +243,110 @@ void ViewerDisplayWidget::IncrementSkippedFrames()
|
||||
|
||||
bool ViewerDisplayWidget::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (o != this->inner_widget()) {
|
||||
return super::eventFilter(o, e);
|
||||
}
|
||||
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
QMouseEvent *mouse = static_cast<QMouseEvent*>(e);
|
||||
if (!(mouse->flags() & Qt::MouseEventCreatedDoubleClick)) {
|
||||
if (OnMousePress(mouse)) {
|
||||
if (o == this->inner_widget()) {
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
QMouseEvent *mouse = static_cast<QMouseEvent*>(e);
|
||||
if (!(mouse->flags() & Qt::MouseEventCreatedDoubleClick)) {
|
||||
if (OnMousePress(mouse)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
EmitColorAtCursor(static_cast<QMouseEvent*>(e));
|
||||
if (OnMouseMove(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (OnMouseRelease(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
if (OnMouseDoubleClick(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::ShortcutOverride:
|
||||
case QEvent::KeyPress:
|
||||
if (OnKeyPress(static_cast<QKeyEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::KeyRelease:
|
||||
if (OnKeyRelease(static_cast<QKeyEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::DragEnter:
|
||||
{
|
||||
auto drag_enter = static_cast<QDragEnterEvent*>(e);
|
||||
if (text_edit_) {
|
||||
ForwardDragEventToTextEdit(drag_enter);
|
||||
} else {
|
||||
emit DragEntered(drag_enter);
|
||||
}
|
||||
|
||||
if (drag_enter->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
EmitColorAtCursor(static_cast<QMouseEvent*>(e));
|
||||
if (OnMouseMove(static_cast<QMouseEvent*>(e))) {
|
||||
case QEvent::DragMove:
|
||||
{
|
||||
auto drag_move = static_cast<QDragMoveEvent*>(e);
|
||||
if (text_edit_) {
|
||||
ForwardDragEventToTextEdit(drag_move);
|
||||
}
|
||||
|
||||
if (drag_move->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::DragLeave:
|
||||
{
|
||||
auto drag_leave = static_cast<QDragLeaveEvent*>(e);
|
||||
if (text_edit_) {
|
||||
ForwardDragEventToTextEdit(drag_leave);
|
||||
} else {
|
||||
emit DragLeft(drag_leave);
|
||||
}
|
||||
|
||||
if (drag_leave->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::Drop:
|
||||
{
|
||||
auto drop = static_cast<QDropEvent*>(e);
|
||||
if (text_edit_) {
|
||||
ForwardDragEventToTextEdit(drop);
|
||||
} else {
|
||||
emit Dropped(drop);
|
||||
}
|
||||
|
||||
if (drop->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (o == text_edit_) {
|
||||
switch (e->type()) {
|
||||
case QEvent::Paint:
|
||||
update();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (OnMouseRelease(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
if (OnMouseDoubleClick(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::DragEnter:
|
||||
emit DragEntered(static_cast<QDragEnterEvent*>(e));
|
||||
break;
|
||||
case QEvent::DragLeave:
|
||||
emit DragLeft(static_cast<QDragLeaveEvent*>(e));
|
||||
break;
|
||||
case QEvent::Drop:
|
||||
emit Dropped(static_cast<QDropEvent*>(e));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super::eventFilter(o, e);
|
||||
@@ -385,6 +450,16 @@ void ViewerDisplayWidget::OnPaint()
|
||||
gizmo->Draw(&p);
|
||||
}
|
||||
}
|
||||
|
||||
if (text_edit_) {
|
||||
QPixmap pm(text_edit_->width(), text_edit_->height());
|
||||
pm.fill(Qt::transparent);
|
||||
|
||||
QPainter pixp(&pm);
|
||||
text_edit_->Paint(&pixp, active_text_gizmo_->GetVerticalAlignment());
|
||||
|
||||
p.drawPixmap(text_edit_pos_, pm);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw action/title safe areas
|
||||
@@ -622,99 +697,95 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPo
|
||||
|
||||
void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event)
|
||||
{
|
||||
QTransform gizmo_transform = GenerateDisplayTransform();
|
||||
|
||||
// Create popup container for text and toolbar
|
||||
auto popup = new QWidget(this);
|
||||
popup->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
|
||||
popup->setAttribute(Qt::WA_DeleteOnClose);
|
||||
popup->setAttribute(Qt::WA_TranslucentBackground);
|
||||
active_text_gizmo_ = text;
|
||||
text_transform_ = GenerateGizmoTransform();
|
||||
text_transform_inverted_ = text_transform_.inverted();
|
||||
|
||||
// Create text editor
|
||||
ViewerTextEditor *text_edit = new ViewerTextEditor(gizmo_transform.m11(), popup);
|
||||
Html::HtmlToDoc(text_edit->document(), text->GetHtml());
|
||||
text_edit->setProperty("gizmo", reinterpret_cast<quintptr>(text));
|
||||
connect(text_edit, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
connect(text_edit, &ViewerTextEditor::destroyed, this, &ViewerDisplayWidget::TextEditDestroyed);
|
||||
text_edit_ = new ViewerTextEditor(text_transform_.m11(), this);
|
||||
|
||||
// Set text editor's gizmo property for later use
|
||||
text_edit_->setProperty("gizmo", reinterpret_cast<quintptr>(text));
|
||||
|
||||
// Install ourselves as event filter so we can receive the text editor's paint events
|
||||
text_edit_->installEventFilter(this);
|
||||
|
||||
// Disable focus on text editor
|
||||
text_edit_->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
// Disable mouse events on text editor
|
||||
text_edit_->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
// "Show" text editor so that it throws paint events, even though its paint event is disabled
|
||||
text_edit_->show();
|
||||
|
||||
// Convert HTML to Qt document
|
||||
Html::HtmlToDoc(text_edit_->document(), text->GetHtml());
|
||||
|
||||
// Connect text change event to propagate back to node
|
||||
connect(text_edit_, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
|
||||
// Connect destroyed signal to cleanup after destruction
|
||||
connect(text_edit_, &ViewerTextEditor::destroyed, this, &ViewerDisplayWidget::TextEditDestroyed);
|
||||
|
||||
// Set text editor's size to logical size
|
||||
QRectF text_rect = text->GetRect();
|
||||
text_edit_pos_ = text_rect.topLeft();
|
||||
text_edit_->setGeometry(text_rect.toRect());
|
||||
|
||||
// Emit text gizmo activation signal
|
||||
emit text->Activated();
|
||||
|
||||
// Get on screen text rect (this will be the text editor's global geometry)
|
||||
QRect global_text_area = gizmo_transform.map(text->GetRect()).boundingRect().toRect();
|
||||
global_text_area = QRect(mapToGlobal(global_text_area.topLeft()), mapToGlobal(global_text_area.bottomRight()));
|
||||
|
||||
QRect global_popup_area = global_text_area;
|
||||
|
||||
// Create toolbar
|
||||
ViewerTextEditorToolBar *toolbar = new ViewerTextEditorToolBar(popup);
|
||||
text_edit->ConnectToolBar(toolbar);
|
||||
text_toolbar_ = new ViewerTextEditorToolBar(text_edit_);
|
||||
text_toolbar_->setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
|
||||
connect(text_toolbar_, &ViewerTextEditorToolBar::VerticalAlignmentChanged, text, &TextGizmo::SetVerticalAlignment);
|
||||
connect(text, &TextGizmo::VerticalAlignmentChanged, text_toolbar_, &ViewerTextEditorToolBar::SetVerticalAlignment);
|
||||
text_toolbar_->SetVerticalAlignment(text->GetVerticalAlignment());
|
||||
text_edit_->ConnectToolBar(text_toolbar_);
|
||||
|
||||
// Work out which corner of the text editor to anchor the toolbar to based on screen limitations
|
||||
bool top = true;
|
||||
bool left = true;
|
||||
for (QScreen *screen : qApp->screens()) {
|
||||
// Look for screen that contains text area
|
||||
if (screen->geometry().contains(global_text_area)) {
|
||||
if (global_text_area.left() + toolbar->width() > screen->geometry().right()) {
|
||||
left = false;
|
||||
}
|
||||
if (global_text_area.top() - toolbar->height() < screen->geometry().top()) {
|
||||
top = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QPoint toolbar_pos;
|
||||
|
||||
if (top) {
|
||||
global_popup_area.adjust(0, -toolbar->height(), 0, 0);
|
||||
toolbar_pos.setY(0);
|
||||
} else {
|
||||
global_popup_area.adjust(0, 0, 0, toolbar->height());
|
||||
toolbar_pos.setY(global_text_area.height());
|
||||
}
|
||||
|
||||
if (toolbar->width() > global_popup_area.width()) {
|
||||
int diff = toolbar->width() - global_popup_area.width();
|
||||
if (left) {
|
||||
global_popup_area.adjust(0, 0, diff, 0);
|
||||
QPoint toolbar_pos = mapToGlobal(text_transform_.map(text_edit_pos_).toPoint());
|
||||
if (QScreen *screen = qApp->screenAt(toolbar_pos)) {
|
||||
// Determine whether to anchor to the top of the rect of the bottom
|
||||
if (toolbar_pos.y() - text_toolbar_->height() >= screen->geometry().top()) {
|
||||
toolbar_pos.setY(toolbar_pos.y() - text_toolbar_->height());
|
||||
} else {
|
||||
global_popup_area.adjust(-diff, 0, 0, 0);
|
||||
toolbar_pos.setY(toolbar_pos.y() + text_transform_.map(text_rect).boundingRect().height());
|
||||
}
|
||||
|
||||
// Clamp X
|
||||
if (toolbar_pos.x() + text_toolbar_->width() > screen->geometry().right()) {
|
||||
toolbar_pos.setX(screen->geometry().right() - text_toolbar_->width());
|
||||
}
|
||||
|
||||
// Clamp Y
|
||||
if (toolbar_pos.y() + text_toolbar_->height() > screen->geometry().bottom()) {
|
||||
toolbar_pos.setY(screen->geometry().bottom() - text_toolbar_->height());
|
||||
}
|
||||
toolbar_pos.setX(0);
|
||||
} else {
|
||||
if (left) {
|
||||
toolbar_pos.setX(0);
|
||||
} else {
|
||||
toolbar_pos.setX(global_popup_area.width() - toolbar->width());
|
||||
}
|
||||
// Fallback
|
||||
toolbar_pos.setY(toolbar_pos.y() - text_toolbar_->height());
|
||||
}
|
||||
|
||||
toolbar->move(toolbar_pos);
|
||||
text_toolbar_->move(toolbar_pos);
|
||||
text_toolbar_->show();
|
||||
|
||||
popup->setGeometry(global_popup_area);
|
||||
// Allow widget to take keyboard focus
|
||||
inner_widget()->setFocusPolicy(Qt::StrongFocus);
|
||||
inner_widget()->setMouseTracking(true);
|
||||
|
||||
text_edit->setGeometry(QRect(text_edit->mapFromGlobal(global_text_area.topLeft()), text_edit->mapFromGlobal(global_text_area.bottomRight())));
|
||||
connect(qApp, &QApplication::focusChanged, this, &ViewerDisplayWidget::FocusChanged);
|
||||
|
||||
popup->show();
|
||||
|
||||
// Store click pos from event so we can use it later to set the initial text cursor position
|
||||
QPoint click_pos;
|
||||
// Start text cursor where the user clicked
|
||||
if (event) {
|
||||
click_pos = event->globalPos();
|
||||
QPoint click_pos = text_transform_inverted_.map(event->pos()) - text_edit_pos_.toPoint();
|
||||
text_edit_->setTextCursor(text_edit_->cursorForPosition(click_pos));
|
||||
}
|
||||
|
||||
// Ensure text edit is actually focused rather than the toolbar
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FirstPaint, this, [text_edit, click_pos]{
|
||||
// Grab focus back from the toolbar
|
||||
text_edit->setFocus();
|
||||
|
||||
// Start text cursor where the user clicked
|
||||
if (!click_pos.isNull()) {
|
||||
text_edit->setTextCursor(text_edit->cursorForPosition(text_edit->mapFromGlobal(click_pos)));
|
||||
}
|
||||
// Grab focus back from the toolbar
|
||||
connect(text_toolbar_, &ViewerTextEditorToolBar::FirstPaint, this, [this]{
|
||||
Core::instance()->main_window()->activateWindow();
|
||||
inner_widget()->setFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -726,10 +797,14 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event)
|
||||
hand_last_drag_pos_ = event->pos();
|
||||
hand_dragging_ = true;
|
||||
emit HandDragStarted();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
inner_widget()->setCursor(Qt::ClosedHandCursor);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (text_edit_) {
|
||||
|
||||
return ForwardMouseEventToTextEdit(event, true);
|
||||
|
||||
} else if (event->button() == Qt::LeftButton) {
|
||||
|
||||
if (Core::instance()->tool() == Tool::kAdd
|
||||
@@ -775,6 +850,19 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event)
|
||||
|
||||
return true;
|
||||
|
||||
} else if (text_edit_) {
|
||||
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
QPointF mapped = text_transform_inverted_.map(event->pos()) - text_edit_pos_;
|
||||
if (mapped.x() >= 0 && mapped.y() >= 0 && mapped.x() < text_edit_->width() && mapped.y() < text_edit_->height()) {
|
||||
inner_widget()->setCursor(Qt::IBeamCursor);
|
||||
} else {
|
||||
inner_widget()->unsetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
return ForwardMouseEventToTextEdit(event);
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
add_band_end_ = event->pos();
|
||||
@@ -832,6 +920,10 @@ bool ViewerDisplayWidget::OnMouseRelease(QMouseEvent *e)
|
||||
|
||||
return true;
|
||||
|
||||
} else if (text_edit_) {
|
||||
|
||||
return ForwardMouseEventToTextEdit(e);
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
QRect band_rect = QRect(add_band_start_, add_band_end_).normalized();
|
||||
@@ -865,7 +957,9 @@ bool ViewerDisplayWidget::OnMouseRelease(QMouseEvent *e)
|
||||
|
||||
bool ViewerDisplayWidget::OnMouseDoubleClick(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && gizmos_) {
|
||||
if (text_edit_) {
|
||||
return ForwardMouseEventToTextEdit(event);
|
||||
} else if (event->button() == Qt::LeftButton && gizmos_) {
|
||||
QPointF ptr = TransformViewerSpaceToBufferSpace(event->pos());
|
||||
foreach (NodeGizmo *g, gizmos_->GetGizmos()) {
|
||||
if (TextGizmo *text = dynamic_cast<TextGizmo*>(g)) {
|
||||
@@ -880,6 +974,27 @@ bool ViewerDisplayWidget::OnMouseDoubleClick(QMouseEvent *event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnKeyPress(QKeyEvent *e)
|
||||
{
|
||||
if (text_edit_) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
CloseTextEditor();
|
||||
return true;
|
||||
} else {
|
||||
return ForwardEventToTextEdit(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnKeyRelease(QKeyEvent *e)
|
||||
{
|
||||
if (text_edit_) {
|
||||
return ForwardEventToTextEdit(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
|
||||
{
|
||||
// Do this no matter what, emits signal to any pixel samplers
|
||||
@@ -987,6 +1102,89 @@ void ViewerDisplayWidget::DrawSubtitleTracks()
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ViewerDisplayWidget::ForwardDragEventToTextEdit(T *e)
|
||||
{
|
||||
// HACK: Absolutely filthy hack. We need to be able to transform the mouse coordinates for our
|
||||
// proxied QTextEdit, however unlike QMouseEvents, Qt's drag events don't allow modifying
|
||||
// the position after construction. Unhelpfully, Qt also explicitly forbids users creating
|
||||
// their own drag events because they "rely on Qt's internal state". So in order to forward
|
||||
// drag events, we defy this by creating our own events, but DON'T process them through Qt's
|
||||
// event queue and instead just send them directly to the widget (requiring its protected
|
||||
// drag events to be made public). That way Qt stays happy, because as far as it's
|
||||
// concerned it's only interfacing with this widget, and the QTextEdit gets to receive
|
||||
// transformed events. It's a terrible hack, but seems to work.
|
||||
|
||||
if constexpr (std::is_same_v<T, QDragLeaveEvent>) {
|
||||
text_edit_->dragLeaveEvent(e);
|
||||
} else {
|
||||
T relay(AdjustPosByVAlign(GetVirtualPosForTextEdit(e->posF())).toPoint(),
|
||||
e->possibleActions(),
|
||||
e->mimeData(),
|
||||
e->mouseButtons(),
|
||||
e->keyboardModifiers());
|
||||
|
||||
if (e->type() == QEvent::DragEnter) {
|
||||
text_edit_->dragEnterEvent(static_cast<QDragEnterEvent*>(&relay));
|
||||
} else if (e->type() == QEvent::DragMove) {
|
||||
text_edit_->dragMoveEvent(static_cast<QDragMoveEvent*>(&relay));
|
||||
} else if (e->type() == QEvent::Drop) {
|
||||
text_edit_->dropEvent(&relay);
|
||||
}
|
||||
|
||||
if (relay.isAccepted()) {
|
||||
e->accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::ForwardMouseEventToTextEdit(QMouseEvent *event, bool check_if_outside)
|
||||
{
|
||||
// Transform screen mouse coords to world mouse coords
|
||||
QPointF local_pos = GetVirtualPosForTextEdit(event->localPos());
|
||||
|
||||
if (check_if_outside) {
|
||||
if (local_pos.x() < 0 || local_pos.x() >= text_edit_->width() || local_pos.y() < 0 || local_pos.y() >= text_edit_->height()) {
|
||||
CloseTextEditor();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
local_pos = AdjustPosByVAlign(local_pos);
|
||||
|
||||
event->setLocalPos(local_pos);
|
||||
return ForwardEventToTextEdit(event);
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::ForwardEventToTextEdit(QEvent *event)
|
||||
{
|
||||
qApp->sendEvent(text_edit_->viewport(), event);
|
||||
return event->isAccepted();
|
||||
}
|
||||
|
||||
QPointF ViewerDisplayWidget::AdjustPosByVAlign(QPointF p)
|
||||
{
|
||||
switch (active_text_gizmo_->GetVerticalAlignment()) {
|
||||
case Qt::AlignTop:
|
||||
// Do nothing
|
||||
break;
|
||||
case Qt::AlignVCenter:
|
||||
p.setY(p.y() - text_edit_->height()/2 + text_edit_->document()->size().height()/2);
|
||||
break;
|
||||
case Qt::AlignBottom:
|
||||
p.setY(p.y() - text_edit_->height() + text_edit_->document()->size().height());
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::CloseTextEditor()
|
||||
{
|
||||
text_edit_->deleteLater();
|
||||
text_edit_ = nullptr;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetShowFPS(bool e)
|
||||
{
|
||||
show_fps_ = e;
|
||||
@@ -1098,6 +1296,12 @@ void ViewerDisplayWidget::TextEditDestroyed()
|
||||
{
|
||||
TextGizmo *gizmo = reinterpret_cast<TextGizmo*>(sender()->property("gizmo").value<quintptr>());
|
||||
emit gizmo->Deactivated();
|
||||
text_edit_ = nullptr;
|
||||
text_toolbar_ = nullptr;
|
||||
inner_widget()->setMouseTracking(false);
|
||||
inner_widget()->setFocusPolicy(Qt::NoFocus);
|
||||
UpdateCursor();
|
||||
disconnect(qApp, &QApplication::focusChanged, this, &ViewerDisplayWidget::FocusChanged);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SubtitlesChanged(const TimeRange &r)
|
||||
@@ -1107,4 +1311,27 @@ void ViewerDisplayWidget::SubtitlesChanged(const TimeRange &r)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::FocusChanged(QWidget *old, QWidget *now)
|
||||
{
|
||||
if (!now) {
|
||||
// Ignore this
|
||||
return;
|
||||
}
|
||||
|
||||
bool unfocused = true;
|
||||
|
||||
while (now) {
|
||||
if (now == text_toolbar_ || now == this) {
|
||||
unfocused = false;
|
||||
break;
|
||||
} else {
|
||||
now = now->parentWidget();
|
||||
}
|
||||
}
|
||||
|
||||
if (unfocused) {
|
||||
CloseTextEditor();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "viewerplaybacktimer.h"
|
||||
#include "viewerqueue.h"
|
||||
#include "viewersafemargininfo.h"
|
||||
#include "viewertexteditor.h"
|
||||
#include "widget/manageddisplay/manageddisplay.h"
|
||||
#include "widget/timetarget/timetarget.h"
|
||||
|
||||
@@ -245,6 +246,12 @@ private:
|
||||
QTransform GenerateDisplayTransform();
|
||||
|
||||
QTransform GenerateGizmoTransform(NodeTraverser >, const TimeRange &range);
|
||||
QTransform GenerateGizmoTransform()
|
||||
{
|
||||
NodeTraverser t;
|
||||
t.SetCacheVideoParams(gizmo_params_);
|
||||
return GenerateGizmoTransform(t, GenerateGizmoTime());
|
||||
}
|
||||
|
||||
TimeRange GenerateGizmoTime()
|
||||
{
|
||||
@@ -261,10 +268,28 @@ private:
|
||||
bool OnMouseRelease(QMouseEvent *e);
|
||||
bool OnMouseDoubleClick(QMouseEvent *e);
|
||||
|
||||
bool OnKeyPress(QKeyEvent *e);
|
||||
bool OnKeyRelease(QKeyEvent *e);
|
||||
|
||||
void EmitColorAtCursor(QMouseEvent* e);
|
||||
|
||||
void DrawSubtitleTracks();
|
||||
|
||||
QPointF GetVirtualPosForTextEdit(const QPointF &p)
|
||||
{
|
||||
return text_transform_inverted_.map(p) - text_edit_pos_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ForwardDragEventToTextEdit(T *event);
|
||||
|
||||
bool ForwardMouseEventToTextEdit(QMouseEvent *event, bool check_if_outside = false);
|
||||
bool ForwardEventToTextEdit(QEvent *event);
|
||||
|
||||
QPointF AdjustPosByVAlign(QPointF p);
|
||||
|
||||
void CloseTextEditor();
|
||||
|
||||
/**
|
||||
* @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL().
|
||||
*/
|
||||
@@ -377,6 +402,13 @@ private:
|
||||
|
||||
bool queue_starved_;
|
||||
|
||||
TextGizmo *active_text_gizmo_;
|
||||
QPointF text_edit_pos_;
|
||||
ViewerTextEditor *text_edit_;
|
||||
ViewerTextEditorToolBar *text_toolbar_;
|
||||
QTransform text_transform_;
|
||||
QTransform text_transform_inverted_;
|
||||
|
||||
private slots:
|
||||
void UpdateFromQueue();
|
||||
|
||||
@@ -385,6 +417,8 @@ private slots:
|
||||
|
||||
void SubtitlesChanged(const TimeRange &r);
|
||||
|
||||
void FocusChanged(QWidget *old, QWidget *now);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
#include <QTextBlock>
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "ui/icons/icons.h"
|
||||
@@ -49,6 +50,9 @@ ViewerTextEditor::ViewerTextEditor(double scale, QWidget *parent) :
|
||||
|
||||
document()->setDefaultStyleSheet(QStringLiteral("body { color: white; }"));
|
||||
|
||||
// Ensure cursor is visible at this scale
|
||||
setCursorWidth(std::ceil(1.0 / scale));
|
||||
|
||||
viewport()->setAutoFillBackground(false);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
@@ -58,7 +62,7 @@ ViewerTextEditor::ViewerTextEditor(double scale, QWidget *parent) :
|
||||
// Force DPI to the same one that we're using in the actual render
|
||||
dpi_force_ = QImage(1, 1, QImage::Format_RGBA8888_Premultiplied);
|
||||
|
||||
const int dpm = 3780 * scale;
|
||||
const int dpm = 3780;
|
||||
dpi_force_.setDotsPerMeterX(dpm);
|
||||
dpi_force_.setDotsPerMeterY(dpm);
|
||||
document()->documentLayout()->setPaintDevice(&dpi_force_);
|
||||
@@ -93,16 +97,13 @@ void ViewerTextEditor::ConnectToolBar(ViewerTextEditorToolBar *toolbar)
|
||||
toolbars_.append(toolbar);
|
||||
}
|
||||
|
||||
void ViewerTextEditor::paintEvent(QPaintEvent *e)
|
||||
void ViewerTextEditor::Paint(QPainter *p, Qt::Alignment valign)
|
||||
{
|
||||
QPainter p(this->viewport());
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
|
||||
QRect r = e->rect();
|
||||
if (r.isValid())
|
||||
p.setClipRect(r, Qt::IntersectClip);
|
||||
ctx.clip = r;
|
||||
QRect clip = this->rect();
|
||||
p->setClipRect(clip, Qt::IntersectClip);
|
||||
ctx.clip = clip;
|
||||
|
||||
ctx.cursorPosition = this->textCursor().position();
|
||||
|
||||
@@ -124,9 +125,29 @@ void ViewerTextEditor::paintEvent(QPaintEvent *e)
|
||||
ctx.selections.append(selection);
|
||||
}
|
||||
|
||||
if (transparent_clone_) {
|
||||
transparent_clone_->documentLayout()->draw(&p, ctx);
|
||||
switch (valign) {
|
||||
case Qt::AlignTop:
|
||||
// Do nothing
|
||||
break;
|
||||
case Qt::AlignVCenter:
|
||||
p->translate(0, clip.height()/2-document()->size().height()/2);
|
||||
break;
|
||||
case Qt::AlignBottom:
|
||||
p->translate(0, clip.height()-document()->size().height());
|
||||
break;
|
||||
}
|
||||
|
||||
const bool use_transparent_clone = true;
|
||||
if (transparent_clone_ && use_transparent_clone) {
|
||||
transparent_clone_->documentLayout()->draw(p, ctx);
|
||||
} else {
|
||||
document()->documentLayout()->draw(p, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerTextEditor::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
// Disable painting
|
||||
}
|
||||
|
||||
void ViewerTextEditor::UpdateToolBar(ViewerTextEditorToolBar *toolbar, const QTextCharFormat &f, const QTextBlockFormat &b, Qt::Alignment alignment)
|
||||
@@ -292,6 +313,7 @@ void ViewerTextEditor::DocumentChanged()
|
||||
delete transparent_clone_;
|
||||
transparent_clone_ = document()->clone(this);
|
||||
transparent_clone_->documentLayout()->setPaintDevice(&dpi_force_);
|
||||
transparent_clone_->documentLayout()->setProperty("cursorWidth", document()->documentLayout()->property("cursorWidth"));
|
||||
|
||||
QTextCursor cursor(transparent_clone_);
|
||||
cursor.select(QTextCursor::Document);
|
||||
@@ -304,132 +326,159 @@ void ViewerTextEditor::DocumentChanged()
|
||||
ViewerTextEditorToolBar::ViewerTextEditorToolBar(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
painted_(false),
|
||||
drag_enabled_(false)
|
||||
drag_enabled_(true)
|
||||
{
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
outer_layout->setSpacing(0);
|
||||
|
||||
QHBoxLayout *basic_layout = new QHBoxLayout();
|
||||
outer_layout->addLayout(basic_layout);
|
||||
const int advanced_slider_width = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("9999.9%"));
|
||||
|
||||
int advanced_slider_width = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("9999.9%"));
|
||||
{
|
||||
QHBoxLayout *row_layout = new QHBoxLayout();
|
||||
row_layout->setSpacing(0);
|
||||
outer_layout->addLayout(row_layout);
|
||||
|
||||
font_combo_ = new QFontComboBox();
|
||||
connect(font_combo_, &QFontComboBox::currentTextChanged, this, &ViewerTextEditorToolBar::UpdateFontStyleListAndEmitFamilyChanged);
|
||||
basic_layout->addWidget(font_combo_);
|
||||
font_combo_ = new QFontComboBox();
|
||||
connect(font_combo_, &QFontComboBox::currentTextChanged, this, &ViewerTextEditorToolBar::UpdateFontStyleListAndEmitFamilyChanged);
|
||||
row_layout->addWidget(font_combo_);
|
||||
|
||||
font_sz_slider_ = new FloatSlider();
|
||||
font_sz_slider_->SetMinimum(0.1);
|
||||
font_sz_slider_->SetMaximum(9999.9);
|
||||
font_sz_slider_->SetDecimalPlaces(1);
|
||||
font_sz_slider_->SetAlignment(Qt::AlignCenter);
|
||||
font_sz_slider_->setFixedWidth(advanced_slider_width);
|
||||
connect(font_sz_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::SizeChanged);
|
||||
font_sz_slider_->SetLadderElementCount(2);
|
||||
basic_layout->addWidget(font_sz_slider_);
|
||||
font_sz_slider_ = new FloatSlider();
|
||||
font_sz_slider_->SetMinimum(0.1);
|
||||
font_sz_slider_->SetMaximum(9999.9);
|
||||
font_sz_slider_->SetDecimalPlaces(1);
|
||||
font_sz_slider_->SetAlignment(Qt::AlignCenter);
|
||||
font_sz_slider_->setFixedWidth(advanced_slider_width);
|
||||
connect(font_sz_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::SizeChanged);
|
||||
font_sz_slider_->SetLadderElementCount(2);
|
||||
row_layout->addWidget(font_sz_slider_);
|
||||
|
||||
style_combo_ = new QComboBox();
|
||||
connect(style_combo_, &QComboBox::currentTextChanged, this, &ViewerTextEditorToolBar::StyleChanged);
|
||||
basic_layout->addWidget(style_combo_);
|
||||
style_combo_ = new QComboBox();
|
||||
connect(style_combo_, &QComboBox::currentTextChanged, this, &ViewerTextEditorToolBar::StyleChanged);
|
||||
row_layout->addWidget(style_combo_);
|
||||
|
||||
underline_btn_ = new QPushButton();
|
||||
connect(underline_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::UnderlineChanged);
|
||||
underline_btn_->setCheckable(true);
|
||||
underline_btn_->setIcon(icon::TextUnderline);
|
||||
basic_layout->addWidget(underline_btn_);
|
||||
underline_btn_ = new QPushButton();
|
||||
connect(underline_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::UnderlineChanged);
|
||||
underline_btn_->setCheckable(true);
|
||||
underline_btn_->setIcon(icon::TextUnderline);
|
||||
row_layout->addWidget(underline_btn_);
|
||||
|
||||
strikethrough_btn_ = new QPushButton();
|
||||
connect(strikethrough_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::StrikethroughChanged);
|
||||
strikethrough_btn_->setCheckable(true);
|
||||
strikethrough_btn_->setIcon(icon::TextStrikethrough);
|
||||
basic_layout->addWidget(strikethrough_btn_);
|
||||
strikethrough_btn_ = new QPushButton();
|
||||
connect(strikethrough_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::StrikethroughChanged);
|
||||
strikethrough_btn_->setCheckable(true);
|
||||
strikethrough_btn_->setIcon(icon::TextStrikethrough);
|
||||
row_layout->addWidget(strikethrough_btn_);
|
||||
|
||||
basic_layout->addWidget(QtUtils::CreateVerticalLine());
|
||||
AddSpacer(row_layout);
|
||||
|
||||
align_left_btn_ = new QPushButton();
|
||||
align_left_btn_->setCheckable(true);
|
||||
align_left_btn_->setIcon(icon::TextAlignLeft);
|
||||
connect(align_left_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignLeft);});
|
||||
basic_layout->addWidget(align_left_btn_);
|
||||
color_btn_ = new QPushButton();
|
||||
color_btn_->setAutoFillBackground(true);
|
||||
connect(color_btn_, &QPushButton::clicked, this, [this]{
|
||||
QColor c = color_btn_->property("color").value<QColor>();
|
||||
|
||||
align_center_btn_ = new QPushButton();
|
||||
align_center_btn_->setCheckable(true);
|
||||
align_center_btn_->setIcon(icon::TextAlignCenter);
|
||||
connect(align_center_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignCenter);});
|
||||
basic_layout->addWidget(align_center_btn_);
|
||||
QColorDialog cd(c, this);
|
||||
if (cd.exec() == QDialog::Accepted) {
|
||||
c = cd.selectedColor();
|
||||
SetColor(c);
|
||||
emit ColorChanged(c);
|
||||
}
|
||||
});
|
||||
row_layout->addWidget(color_btn_);
|
||||
|
||||
align_right_btn_ = new QPushButton();
|
||||
align_right_btn_->setCheckable(true);
|
||||
align_right_btn_->setIcon(icon::TextAlignRight);
|
||||
connect(align_right_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignRight);});
|
||||
basic_layout->addWidget(align_right_btn_);
|
||||
row_layout->addStretch();
|
||||
}
|
||||
|
||||
align_justify_btn_ = new QPushButton();
|
||||
align_justify_btn_->setCheckable(true);
|
||||
align_justify_btn_->setIcon(icon::TextAlignJustify);
|
||||
connect(align_justify_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignJustify);});
|
||||
basic_layout->addWidget(align_justify_btn_);
|
||||
{
|
||||
QHBoxLayout *row_layout = new QHBoxLayout();
|
||||
row_layout->setSpacing(0);
|
||||
outer_layout->addLayout(row_layout);
|
||||
|
||||
basic_layout->addWidget(QtUtils::CreateVerticalLine());
|
||||
align_left_btn_ = new QPushButton();
|
||||
align_left_btn_->setCheckable(true);
|
||||
align_left_btn_->setIcon(icon::TextAlignLeft);
|
||||
connect(align_left_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignLeft);});
|
||||
row_layout->addWidget(align_left_btn_);
|
||||
|
||||
color_btn_ = new QPushButton();
|
||||
color_btn_->setAutoFillBackground(true);
|
||||
connect(color_btn_, &QPushButton::clicked, this, [this]{
|
||||
QColor c = color_btn_->property("color").value<QColor>();
|
||||
align_center_btn_ = new QPushButton();
|
||||
align_center_btn_->setCheckable(true);
|
||||
align_center_btn_->setIcon(icon::TextAlignCenter);
|
||||
connect(align_center_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignHCenter);});
|
||||
row_layout->addWidget(align_center_btn_);
|
||||
|
||||
QColorDialog cd(c, this);
|
||||
if (cd.exec() == QDialog::Accepted) {
|
||||
c = cd.selectedColor();
|
||||
SetColor(c);
|
||||
emit ColorChanged(c);
|
||||
}
|
||||
});
|
||||
basic_layout->addWidget(color_btn_);
|
||||
align_right_btn_ = new QPushButton();
|
||||
align_right_btn_->setCheckable(true);
|
||||
align_right_btn_->setIcon(icon::TextAlignRight);
|
||||
connect(align_right_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignRight);});
|
||||
row_layout->addWidget(align_right_btn_);
|
||||
|
||||
basic_layout->addStretch();
|
||||
align_justify_btn_ = new QPushButton();
|
||||
align_justify_btn_->setCheckable(true);
|
||||
align_justify_btn_->setIcon(icon::TextAlignJustify);
|
||||
connect(align_justify_btn_, &QPushButton::clicked, this, [this]{emit AlignmentChanged(Qt::AlignJustify);});
|
||||
row_layout->addWidget(align_justify_btn_);
|
||||
|
||||
QHBoxLayout *advanced_layout = new QHBoxLayout();
|
||||
outer_layout->addLayout(advanced_layout);
|
||||
AddSpacer(row_layout);
|
||||
|
||||
advanced_layout->addWidget(new QLabel(tr("Stretch: "))); // FIXME: Procure icon
|
||||
align_top_btn_ = new QPushButton();
|
||||
align_top_btn_->setCheckable(true);
|
||||
align_top_btn_->setIcon(icon::TextAlignTop);
|
||||
connect(align_top_btn_, &QPushButton::clicked, this, [this]{emit VerticalAlignmentChanged(Qt::AlignTop);});
|
||||
row_layout->addWidget(align_top_btn_);
|
||||
|
||||
stretch_slider_ = new IntegerSlider();
|
||||
stretch_slider_->SetMinimum(0);
|
||||
stretch_slider_->SetDefaultValue(100);
|
||||
stretch_slider_->setFixedWidth(advanced_slider_width);
|
||||
stretch_slider_->SetFormat(tr("%1%"));
|
||||
connect(stretch_slider_, &IntegerSlider::ValueChanged, this, &ViewerTextEditorToolBar::StretchChanged);
|
||||
advanced_layout->addWidget(stretch_slider_);
|
||||
align_middle_btn_ = new QPushButton();
|
||||
align_middle_btn_->setCheckable(true);
|
||||
align_middle_btn_->setIcon(icon::TextAlignMiddle);
|
||||
connect(align_middle_btn_, &QPushButton::clicked, this, [this]{emit VerticalAlignmentChanged(Qt::AlignVCenter);});
|
||||
row_layout->addWidget(align_middle_btn_);
|
||||
|
||||
advanced_layout->addWidget(new QLabel(tr("Kerning: "))); // FIXME: Procure icon
|
||||
align_bottom_btn_ = new QPushButton();
|
||||
align_bottom_btn_->setCheckable(true);
|
||||
align_bottom_btn_->setIcon(icon::TextAlignBottom);
|
||||
connect(align_bottom_btn_, &QPushButton::clicked, this, [this]{emit VerticalAlignmentChanged(Qt::AlignBottom);});
|
||||
row_layout->addWidget(align_bottom_btn_);
|
||||
|
||||
kerning_slider_ = new FloatSlider();
|
||||
kerning_slider_->SetMinimum(0);
|
||||
kerning_slider_->SetDefaultValue(100);
|
||||
kerning_slider_->SetDecimalPlaces(1);
|
||||
kerning_slider_->setFixedWidth(advanced_slider_width);
|
||||
kerning_slider_->SetFormat(tr("%1%"));
|
||||
connect(kerning_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::KerningChanged);
|
||||
advanced_layout->addWidget(kerning_slider_);
|
||||
AddSpacer(row_layout);
|
||||
|
||||
advanced_layout->addWidget(new QLabel(tr("Line Height: "))); // FIXME: Procure icon
|
||||
small_caps_btn_ = new QPushButton();
|
||||
small_caps_btn_->setIcon(icon::TextSmallCaps);
|
||||
small_caps_btn_->setCheckable(true);
|
||||
connect(small_caps_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::SmallCapsChanged);
|
||||
row_layout->addWidget(small_caps_btn_);
|
||||
|
||||
line_height_slider_ = new FloatSlider();
|
||||
line_height_slider_->SetMinimum(0);
|
||||
line_height_slider_->SetDefaultValue(100);
|
||||
line_height_slider_->SetDecimalPlaces(1);
|
||||
line_height_slider_->setFixedWidth(advanced_slider_width);
|
||||
line_height_slider_->SetFormat(tr("%1%"));
|
||||
connect(line_height_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::LineHeightChanged);
|
||||
advanced_layout->addWidget(line_height_slider_);
|
||||
AddSpacer(row_layout);
|
||||
|
||||
small_caps_btn_ = new QPushButton();
|
||||
small_caps_btn_->setIcon(icon::TextSmallCaps);
|
||||
small_caps_btn_->setCheckable(true);
|
||||
connect(small_caps_btn_, &QPushButton::clicked, this, &ViewerTextEditorToolBar::SmallCapsChanged);
|
||||
advanced_layout->addWidget(small_caps_btn_);
|
||||
row_layout->addWidget(new QLabel(tr("Stretch: "))); // FIXME: Procure icon
|
||||
|
||||
advanced_layout->addStretch();
|
||||
stretch_slider_ = new IntegerSlider();
|
||||
stretch_slider_->SetMinimum(0);
|
||||
stretch_slider_->SetDefaultValue(100);
|
||||
stretch_slider_->setFixedWidth(advanced_slider_width);
|
||||
stretch_slider_->SetFormat(tr("%1%"));
|
||||
connect(stretch_slider_, &IntegerSlider::ValueChanged, this, &ViewerTextEditorToolBar::StretchChanged);
|
||||
row_layout->addWidget(stretch_slider_);
|
||||
|
||||
row_layout->addWidget(new QLabel(tr("Kerning: "))); // FIXME: Procure icon
|
||||
|
||||
kerning_slider_ = new FloatSlider();
|
||||
kerning_slider_->SetMinimum(0);
|
||||
kerning_slider_->SetDefaultValue(100);
|
||||
kerning_slider_->SetDecimalPlaces(1);
|
||||
kerning_slider_->setFixedWidth(advanced_slider_width);
|
||||
kerning_slider_->SetFormat(tr("%1%"));
|
||||
connect(kerning_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::KerningChanged);
|
||||
row_layout->addWidget(kerning_slider_);
|
||||
|
||||
row_layout->addWidget(new QLabel(tr("Line Height: "))); // FIXME: Procure icon
|
||||
|
||||
line_height_slider_ = new FloatSlider();
|
||||
line_height_slider_->SetMinimum(0);
|
||||
line_height_slider_->SetDefaultValue(100);
|
||||
line_height_slider_->SetDecimalPlaces(1);
|
||||
line_height_slider_->setFixedWidth(advanced_slider_width);
|
||||
line_height_slider_->SetFormat(tr("%1%"));
|
||||
connect(line_height_slider_, &FloatSlider::ValueChanged, this, &ViewerTextEditorToolBar::LineHeightChanged);
|
||||
row_layout->addWidget(line_height_slider_);
|
||||
|
||||
row_layout->addStretch();
|
||||
}
|
||||
|
||||
setAutoFillBackground(true);
|
||||
|
||||
@@ -439,11 +488,18 @@ ViewerTextEditorToolBar::ViewerTextEditorToolBar(QWidget *parent) :
|
||||
void ViewerTextEditorToolBar::SetAlignment(Qt::Alignment a)
|
||||
{
|
||||
align_left_btn_->setChecked(a == Qt::AlignLeft);
|
||||
align_center_btn_->setChecked(a == Qt::AlignCenter);
|
||||
align_center_btn_->setChecked(a == Qt::AlignHCenter);
|
||||
align_right_btn_->setChecked(a == Qt::AlignRight);
|
||||
align_justify_btn_->setChecked(a == Qt::AlignJustify);
|
||||
}
|
||||
|
||||
void ViewerTextEditorToolBar::SetVerticalAlignment(Qt::Alignment a)
|
||||
{
|
||||
align_top_btn_->setChecked(a == Qt::AlignTop);
|
||||
align_middle_btn_->setChecked(a == Qt::AlignVCenter);
|
||||
align_bottom_btn_->setChecked(a == Qt::AlignBottom);
|
||||
}
|
||||
|
||||
void ViewerTextEditorToolBar::SetColor(const QColor &c)
|
||||
{
|
||||
color_btn_->setProperty("color", c);
|
||||
@@ -464,6 +520,20 @@ void ViewerTextEditorToolBar::paintEvent(QPaintEvent *event)
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void ViewerTextEditorToolBar::AddSpacer(QLayout *l)
|
||||
{
|
||||
const int spacing = this->fontMetrics().height()/4;
|
||||
QWidget *a = new QWidget();
|
||||
a->setFixedSize(spacing, 1);
|
||||
l->addWidget(a);
|
||||
|
||||
l->addWidget(QtUtils::CreateVerticalLine());
|
||||
|
||||
QWidget *b = new QWidget();
|
||||
b->setFixedSize(spacing, 1);
|
||||
l->addWidget(b);
|
||||
}
|
||||
|
||||
void ViewerTextEditorToolBar::UpdateFontStyleList(const QString &family)
|
||||
{
|
||||
QString temp = style_combo_->currentText();
|
||||
|
||||
@@ -68,6 +68,7 @@ public slots:
|
||||
void SetUnderline(bool e) { underline_btn_->setChecked(e); }
|
||||
void SetStrikethrough(bool e) { strikethrough_btn_->setChecked(e); }
|
||||
void SetAlignment(Qt::Alignment a);
|
||||
void SetVerticalAlignment(Qt::Alignment a);
|
||||
void SetColor(const QColor &c);
|
||||
void SetSmallCaps(bool e) { small_caps_btn_->setChecked(e); }
|
||||
void SetStretch(int i) { stretch_slider_->SetValue(i); }
|
||||
@@ -81,6 +82,7 @@ signals:
|
||||
void UnderlineChanged(bool e);
|
||||
void StrikethroughChanged(bool e);
|
||||
void AlignmentChanged(Qt::Alignment alignment);
|
||||
void VerticalAlignmentChanged(Qt::Alignment alignment);
|
||||
void ColorChanged(const QColor &c);
|
||||
void SmallCapsChanged(bool e);
|
||||
void StretchChanged(int i);
|
||||
@@ -101,6 +103,8 @@ protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
void AddSpacer(QLayout *l);
|
||||
|
||||
QPoint drag_anchor_;
|
||||
|
||||
QFontComboBox *font_combo_;
|
||||
@@ -117,6 +121,10 @@ private:
|
||||
QPushButton *align_right_btn_;
|
||||
QPushButton *align_justify_btn_;
|
||||
|
||||
QPushButton *align_top_btn_;
|
||||
QPushButton *align_middle_btn_;
|
||||
QPushButton *align_bottom_btn_;
|
||||
|
||||
IntegerSlider *stretch_slider_;
|
||||
FloatSlider *kerning_slider_;
|
||||
FloatSlider *line_height_slider_;
|
||||
@@ -145,6 +153,13 @@ public:
|
||||
|
||||
void SetListenToFocusEvents(bool e) { listen_to_focus_events_ = e; }
|
||||
|
||||
void Paint(QPainter *p, Qt::Alignment valign);
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent *e) override { return QTextEdit::dragEnterEvent(e); }
|
||||
virtual void dragMoveEvent(QDragMoveEvent *e) override { return QTextEdit::dragMoveEvent(e); }
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent *e) override { return QTextEdit::dragLeaveEvent(e); }
|
||||
virtual void dropEvent(QDropEvent *e) override { return QTextEdit::dropEvent(e); }
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user