Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
96999d5
FEAT(client,images): Add animated GIF support
GeneralUser01 Dec 7, 2024
9ed2732
CHANGE(client,images): Reorganize code for animations and make variou…
GeneralUser01 Apr 2, 2025
53cc9ea
FIX(client,images): Parse multiple potential animations per message c…
GeneralUser01 Apr 19, 2025
d6cfe27
FIX(client,images): Add bound-checks for when there are no custom tex…
GeneralUser01 Apr 20, 2025
2a4dcdd
CHANGE(client,images): Add file extension APNG to those associated wi…
GeneralUser01 Apr 20, 2025
283335d
CHANGE(client,formats): Detect image formats supported by Qt via file…
GeneralUser01 Apr 21, 2025
266649f
CHANGE(client,formats): Detect more image formats via file header
GeneralUser01 May 1, 2025
6743872
CHANGE(client,formats): Use automatic list of file extensions for ani…
GeneralUser01 May 2, 2025
8322740
CHANGE(client,formats): Use JPG as the default image format and exten…
GeneralUser01 May 5, 2025
91a5067
CHANGE(client,formats): Provide one filter per image format when savi…
GeneralUser01 May 5, 2025
7c62c33
FIX(client,images): Leave scrolling position unchanged when exiting f…
GeneralUser01 May 7, 2025
786aa15
FEAT(client,images): Include animated images when copying from selection
GeneralUser01 May 13, 2025
c46db3d
FEAT(client,images): Enable video control interaction via scrolling
GeneralUser01 May 22, 2025
8db2b43
FIX(client,images): Make the no-loop mode work again by capturing rel…
GeneralUser01 May 22, 2025
258233a
FORMAT(client,images): Use the full name for more functions
GeneralUser01 May 22, 2025
8a8acfd
FORMAT(client,images): Use more specific naming for animated images
GeneralUser01 May 24, 2025
1b323c7
CHANGE(client,images): Re-add string for translation and make error h…
GeneralUser01 May 27, 2025
744bd78
CHANGE(client,images): Make animated images generically compatible wi…
GeneralUser01 Jul 7, 2025
f7f7d93
CHANGE(client,images): Decode percent encoded base64 data for custom …
GeneralUser01 Jul 7, 2025
8f91417
CHANGE(client,images): Use one `bool` per document for whether the vi…
GeneralUser01 Jul 9, 2025
30e0e21
CHANGE(client,images): Do not wrap frame-by-frame traversal
GeneralUser01 Jul 9, 2025
c691a59
CHANGE(client,parameters): Use reference instead of pointer wherever …
GeneralUser01 Jul 10, 2025
9781137
CHANGE(client,context-menu): Make function for reusing video controls…
GeneralUser01 Jul 12, 2025
07ef8c4
CHANGE(client,images): Use the most accurate method of detecting cust…
GeneralUser01 Jul 12, 2025
0abd619
FIX(client,images): Cast the last index of custom objects correctly a…
GeneralUser01 Jul 13, 2025
21b66ed
TRANSLATION: Update translation files
GeneralUser01 Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,899 changes: 1,866 additions & 33 deletions src/mumble/CustomElements.cpp

Large diffs are not rendered by default.

176 changes: 173 additions & 3 deletions src/mumble/CustomElements.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,75 @@
#define MUMBLE_MUMBLE_CUSTOMELEMENTS_H_

#include <QtCore/QObject>
#include <QtGui/QMovie>
#include <QtGui/QTextObjectInterface>
#include <QtWidgets/QLabel>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QTextEdit>

Q_DECLARE_METATYPE(QMovie *)

class LogTextBrowser : public QTextBrowser {
private:
Q_OBJECT
Q_DISABLE_COPY(LogTextBrowser)

protected:
static QWidget *widgetInFullScreen;
QString queuedNumericInput;
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *) Q_DECL_OVERRIDE;
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
void keyReleaseEvent(QKeyEvent *) Q_DECL_OVERRIDE;
QMimeData *createMimeDataFromSelection() const Q_DECL_OVERRIDE;

public:
LogTextBrowser(QWidget *p = nullptr);

int getLogScroll();
void setLogScroll(int scroll_pos);
static void registerCustomTextObjects(QTextDocument &doc);
static void highlightSelectedObject(const QTextDocument &doc, QPainter &painter, const QRect &rect,
const QObject &propertyHolder);
static QRect calcRectScaledToScreen(const QSize &size);
static bool isWidgetInFullScreen();
static void toggleFullScreen(QWidget *widget = nullptr);

static bool mousePressCustomTextObjects(const QTextDocument &doc, const QMouseEvent &mouseEvt);
static bool scrollCustomTextObjects(const QTextDocument &doc, const QWheelEvent &wheelEvt);
static bool keyPressCustomTextObjects(const QTextDocument &doc, const QKeyEvent &keyEvt,
bool isObjSelectionChanged);
static QString addedCustomTextObjectsToHtml(const QString &html, const QTextCursor &tc,
bool isLimitedToSelection = true);

static void clear(QTextDocument &doc, std::function< void() > baseClear = nullptr);
static int getScrollX(const QTextDocument &doc);
static int getScrollY(const QTextDocument &doc);
static QPoint getScrollPos(const QTextDocument &doc);
static QRect getDocumentViewRect(const QTextDocument &doc);
static void setScrollX(const QTextDocument &doc, int scrollPos);
static void setScrollY(const QTextDocument &doc, int scrollPos);
static void changeScrollX(const QTextDocument &doc, int change);
static void changeScrollY(const QTextDocument &doc, int change);
static bool isScrolledToBottom(const QTextDocument &doc);
static void scrollAreaIntoView(const QTextDocument &doc, const QRect &rect);
static QObject *customObjectAt(const QTextDocument &doc, const QPoint &pos);
static QObject *customObjectOfFormat(const QTextFormat &fmt, const QPoint &pos);
static void update(const QTextDocument &doc, const QRect &rect = QRect());
static void reflow(QTextDocument &doc);

void clear();
int getScrollX();
int getScrollY();
QPoint getScrollPos();
QRect getDocumentViewRect();
void setScrollX(int scrollPos);
void setScrollY(int scrollPos);
void changeScrollX(int change);
void changeScrollY(int change);
bool isScrolledToBottom();
void scrollAreaIntoView(const QRect &rect);
QObject *customObjectAt(const QPoint &pos);
void update(const QRect &rect = QRect());
void reflow();
};

class ChatbarTextEdit : public QTextEdit {
Expand Down Expand Up @@ -52,7 +106,7 @@ class ChatbarTextEdit : public QTextEdit {
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool sendImagesFromMimeData(const QMimeData *source);
bool emitPastedImage(QImage image);
bool emitPastedImage(const QImage &image, const QString &filePath = "", const QByteArray &imageBa = QByteArray());

public:
void setDefaultText(const QString &, bool = false);
Expand All @@ -76,6 +130,122 @@ public slots:
ChatbarTextEdit(QWidget *p = nullptr);
};

class VideoUtils {
public:
enum class VideoControl {
None,
View,
VideoBar,
PlayPause,
Cache,
Loop,
PreviousFrame,
NextFrame,
ResetSpeed,
DecreaseSpeed,
IncreaseSpeed,
FullScreen
};
static const int videoBarHeight = 4;
static const int underVideoBarHeight = 20;
static const int videoControlsHeight = videoBarHeight + underVideoBarHeight;
static const int videoBarMinWidth = 260;
static const int cacheOffsetX = -180;
static const int loopModeOffsetX = -140;
static const int frameTraversalOffsetX = -100;
static const int speedOffsetX = -70;
static const int fullScreenOffsetX = -15;
static const int videoControlsRevertDelay = 1200;

static bool isInBoundsOnAxis(int posOnAxis, int start, int length);
static bool isInBounds(const QPoint &pos, const QRect &bounds);
static int calcPosOnAxisFromOffset(int offset, int start, int length);
static QRect calcVideoControlsRect(const QObject &propertyHolder);

static VideoControl videoControlAt(const QPoint &pos, const QRect &rect);
static double videoBarProportionAt(const QPoint &pos, const QRect &rect);
static void drawVideoControls(QPainter &painter, const QRect &rect, const QObject &propertyHolder,
double opacity = 1);
static void drawCenteredPlayIcon(QPainter &painter, const QRect &rect);
static void addVideoControlsTransition(QObject &propertyHolder, QWidget &area, bool isIdleCursorHidden = true);
static void startOrHoldVideoControlsTransition(QObject &propertyHolder, const QPoint &hoveredPos = QPoint(),
QWidget *cursorArea = nullptr);

static void updateVideoControls(const QObject &propertyHolder, QWidget *area = nullptr,
const QMargins &margins = QMargins());
static void updatePropertyRect(QObject &propertyHolder, const QRect &rect);
static QSizeF calcIntrinsicSize(const QSize &size, bool areVideoControlsShown);
static void setPropertyFullScreen(QObject &propertyHolder, bool isShown);
static void setAttributesWidthAndHeight(const QObject &propertyHolder, QSize &size);
};

class ImageAnimationTextObject : public QObject, public QTextObjectInterface {
Q_OBJECT
Q_INTERFACES(QTextObjectInterface)

protected:
static QObject *createImageAnimation(bool &isAnimationCheckOnly, const QByteArray &animationBa,
QTextDocument *parentDoc = nullptr);
QSizeF intrinsicSize(QTextDocument *doc, int charPos, const QTextFormat &fmt) Q_DECL_OVERRIDE;
void drawObject(QPainter *painterPtr, const QRectF &rectF, QTextDocument *doc, int charPos,
const QTextFormat &fmt) Q_DECL_OVERRIDE;

public:
bool areVideoControlsShown = false;
ImageAnimationTextObject(QTextDocument &parentDoc);
static QObject *createImageAnimation(const QByteArray &animationBa, QTextDocument &parentDoc);
static bool testImageAnimation(const QByteArray &animationBa);

static bool mousePress(QMovie &animation, const QPoint &mouseDocPos, const Qt::MouseButton &button,
bool areVideoControlsShown = false);
static bool scroll(QMovie &animation, const QPoint &mouseDocPos, bool isScrollingUp,
bool areVideoControlsShown = false);
static bool keyPress(QMovie &animation, const Qt::Key &key, bool isObjectSelectionChanged = false);

enum class LoopMode { Unchanged, Loop, NoLoop };
static QString loopModeToString(LoopMode mode);

static int getTotalTime(const QObject &propertyHolder);
static int getCurrentTime(const QObject &propertyHolder, int frameIndex);
static void setFrame(QMovie &animation, int frameIndex);
static void setFrameByTime(QMovie &animation, int milliseconds);
static void setFrameByProportion(QMovie &animation, double proportion);
static void changeFrame(QMovie &animation, int amount);
static void changeFrameByTime(QMovie &animation, int milliseconds);
static void togglePause(QMovie &animation);
static void toggleCache(QMovie &animation);
static void toggleFullScreen(QMovie &animation);
static void escapeFullScreen();
static void stopPlayback(QMovie &animation);
static void resetPlayback(QMovie &animation);
static void setSpeed(QMovie &animation, int percentage);
static void resetSpeed(QMovie &animation);
static void invertSpeed(QMovie &animation);
static void changeSpeed(QMovie &animation, int percentageStep);
static void setLoopMode(QMovie &animation, LoopMode mode);
static void changeLoopMode(QMovie &animation, int steps);
static void toggleVideoControlsFullScreen(QObject &propertyHolder);

void toggleVideoControls();
void addVideoControlsSwitch(QMenu &menu);
bool mousePress(const QPoint &mouseDocPos, const Qt::MouseButton &button, QMovie &animation);
bool scroll(const QPoint &mouseDocPos, bool isScrollingUp, QMovie &animation);
};

class FullScreenImageAnimation : public QLabel {
Q_OBJECT

protected:
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *) Q_DECL_OVERRIDE;
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;

public:
FullScreenImageAnimation(QMovie &animation, QWidget *parentWidget = nullptr);
};

class DockTitleBar : public QLabel {
private:
Q_OBJECT
Expand Down
Loading
Loading