Merge branch 'master' of github.com:Chatterino/chatterino2

This commit is contained in:
fourtf 2021-04-14 15:53:40 +02:00
commit 0dd6934f02
42 changed files with 394 additions and 100 deletions

View file

@ -63,29 +63,32 @@ jobs:
version: ${{ matrix.qt-version }} version: ${{ matrix.qt-version }}
# WINDOWS # WINDOWS
- name: Cache conan packages - name: Cache conan packages
if: startsWith(matrix.os, 'windows') if: startsWith(matrix.os, 'windows')
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.4
with: with:
key: ${{ runner.os }}-conan-${{ hashFiles('**/conanfile.txt') }}-20210307 key: ${{ runner.os }}-conan-${{ hashFiles('**/conanfile.txt') }}-20210412
path: C:/.conan/ path: C:/.conan/
- name: Add Conan to path
if: startsWith(matrix.os, 'windows')
run: echo "C:\Program Files\Conan\conan\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install dependencies (Windows) - name: Install dependencies (Windows)
if: startsWith(matrix.os, 'windows') if: startsWith(matrix.os, 'windows')
run: | run: |
choco install conan -y choco install conan -y
refreshenv - name: Enable Developer Command Prompt
shell: cmd if: startsWith(matrix.os, 'windows')
uses: ilammy/msvc-dev-cmd@v1.7.0
- name: Build (Windows) - name: Build (Windows)
if: startsWith(matrix.os, 'windows') && matrix.build-system == 'qmake' if: startsWith(matrix.os, 'windows') && matrix.build-system == 'qmake'
run: | run: |
call "%programfiles(x86)%\Microsoft Visual Studio\%vs_version%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
mkdir build mkdir build
cd build cd build
"C:\Program Files\Conan\conan\conan.exe" install .. conan install ..
qmake .. qmake ..
set cl=/MP set cl=/MP
nmake /S /NOLOGO nmake /S /NOLOGO
@ -93,15 +96,13 @@ jobs:
cp release/chatterino.exe Chatterino2/ cp release/chatterino.exe Chatterino2/
echo nightly > Chatterino2/modes echo nightly > Chatterino2/modes
7z a chatterino-windows-x86-64.zip Chatterino2/ 7z a chatterino-windows-x86-64.zip Chatterino2/
shell: cmd
- name: Build with CMake (Windows) - name: Build with CMake (Windows)
if: startsWith(matrix.os, 'windows') && matrix.build-system == 'cmake' if: startsWith(matrix.os, 'windows') && matrix.build-system == 'cmake'
run: | run: |
call "%programfiles(x86)%\Microsoft Visual Studio\%vs_version%\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
mkdir build mkdir build
cd build cd build
"C:\Program Files\Conan\conan\conan.exe" install .. conan install ..
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_CONAN=ON .. cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_CONAN=ON ..
set cl=/MP set cl=/MP
nmake /S /NOLOGO nmake /S /NOLOGO
@ -109,17 +110,10 @@ jobs:
cp bin/chatterino.exe Chatterino2/ cp bin/chatterino.exe Chatterino2/
echo nightly > Chatterino2/modes echo nightly > Chatterino2/modes
7z a chatterino-windows-x86-64.zip Chatterino2/ 7z a chatterino-windows-x86-64.zip Chatterino2/
shell: cmd
- name: Ensure build succeeded (Windows)
if: startsWith(matrix.os, 'windows')
run: |
cd build
ls Chatterino2/chatterino.exe
- name: Upload artifact (Windows) - name: Upload artifact (Windows)
if: startsWith(matrix.os, 'windows') if: startsWith(matrix.os, 'windows')
uses: actions/upload-artifact@v2.2.2 uses: actions/upload-artifact@v2.2.3
with: with:
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ matrix.build-system }}.zip name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ matrix.build-system }}.zip
path: build/chatterino-windows-x86-64.zip path: build/chatterino-windows-x86-64.zip
@ -176,7 +170,7 @@ jobs:
- name: Upload artifact (Ubuntu) - name: Upload artifact (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') if: startsWith(matrix.os, 'ubuntu')
uses: actions/upload-artifact@v2.2.2 uses: actions/upload-artifact@v2.2.3
with: with:
name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ matrix.build-system }}.AppImage name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ matrix.build-system }}.AppImage
path: build/Chatterino-x86_64.AppImage path: build/Chatterino-x86_64.AppImage
@ -221,7 +215,7 @@ jobs:
- name: Upload artifact (MacOS) - name: Upload artifact (MacOS)
if: startsWith(matrix.os, 'macos') if: startsWith(matrix.os, 'macos')
uses: actions/upload-artifact@v2.2.2 uses: actions/upload-artifact@v2.2.3
with: with:
name: chatterino-osx-${{ matrix.qt-version }}-${{ matrix.build-system }}.dmg name: chatterino-osx-${{ matrix.qt-version }}-${{ matrix.build-system }}.dmg
path: build/chatterino-osx.dmg path: build/chatterino-osx.dmg
@ -245,32 +239,32 @@ jobs:
Nightly Build Nightly Build
prerelease: true prerelease: true
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: chatterino-windows-x86-64-5.15.2-qmake.zip name: chatterino-windows-x86-64-5.15.2-qmake.zip
path: windows/ path: windows/
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: chatterino-windows-x86-64-5.15.2-cmake.zip name: chatterino-windows-x86-64-5.15.2-cmake.zip
path: windows-cmake/ path: windows-cmake/
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: Chatterino-x86_64-5.15.2-qmake.AppImage name: Chatterino-x86_64-5.15.2-qmake.AppImage
path: linux/ path: linux/
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: Chatterino-x86_64-5.15.2-cmake.AppImage name: Chatterino-x86_64-5.15.2-cmake.AppImage
path: linux-cmake/ path: linux-cmake/
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: chatterino-osx-5.15.2-qmake.dmg name: chatterino-osx-5.15.2-qmake.dmg
path: macos/ path: macos/
- uses: actions/download-artifact@v2.0.8 - uses: actions/download-artifact@v2.0.9
with: with:
name: chatterino-osx-5.15.2-cmake.dmg name: chatterino-osx-5.15.2-cmake.dmg
path: macos-cmake/ path: macos-cmake/

3
.gitmodules vendored
View file

@ -25,3 +25,6 @@
[submodule "lib/websocketpp"] [submodule "lib/websocketpp"]
path = lib/websocketpp path = lib/websocketpp
url = https://github.com/ziocleto/websocketpp url = https://github.com/ziocleto/websocketpp
[submodule "cmake/sanitizers-cmake"]
path = cmake/sanitizers-cmake
url = https://github.com/arsenm/sanitizers-cmake

View file

@ -6,8 +6,25 @@ Note on Qt version compatibility: If you are installing Qt from a package manage
_most likely works the same for other Debian-like distros_ _most likely works the same for other Debian-like distros_
1. Install dependencies (and the C++ IDE Qt Creator) `sudo apt install qtcreator qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake` 1. Install dependencies `sudo apt install qttools5-dev qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++`
1. Open `chatterino.pro` with QT Creator and build
### Through Qt Creator
1. Install C++ IDE Qt Creator `sudo apt install qtcreator`
1. Open `chatterino.pro` with Qt Creator and select build
### Manually
1. go into project directory
1. create build folder `mkdir build && cd build`
#### Using QMake
1. `qmake .. && make`
#### Using CMake
1. `cmake .. && make`
## Arch Linux ## Arch Linux
@ -17,15 +34,15 @@ _most likely works the same for other Debian-like distros_
### Manually ### Manually
1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake` 1. `sudo pacman -S qt5-base qt5-multimedia qt5-svg qt5-tools gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake`
1. go into project directory 1. go into project directory
1. create build folder `mkdir build && cd build` 1. create build folder `mkdir build && cd build`
### Using QMake #### Using QMake
1. `qmake .. && make` 1. `qmake .. && make`
### Using CMake #### Using CMake
1. `cmake .. && make` 1. `cmake .. && make`

View file

@ -33,7 +33,7 @@ Note: This installation will take about 1.5 GB of disk space.
### For our websocket library, we need OpenSSL 1.1 ### For our websocket library, we need OpenSSL 1.1
1. Download OpenSSL for windows, version `1.1.1i`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1i.exe)** 1. Download OpenSSL for windows, version `1.1.1j`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1j.exe)**
2. When prompted, install OpenSSL to `C:\local\openssl` 2. When prompted, install OpenSSL to `C:\local\openssl`
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory". 3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory".

View file

@ -5,7 +5,8 @@
- Major: Added clip creation support. You can create clips with `/clip` command, `Alt+X` keybind or `Create a clip` option in split header's context menu. This requires a new authentication scope so re-authentication will be required to use it. (#2271, #2377, #2528) - Major: Added clip creation support. You can create clips with `/clip` command, `Alt+X` keybind or `Create a clip` option in split header's context menu. This requires a new authentication scope so re-authentication will be required to use it. (#2271, #2377, #2528)
- Major: Added "Channel Filters". See https://wiki.chatterino.com/Filters/ for how they work or how to configure them. (#1748, #2083, #2090, #2200, #2225) - Major: Added "Channel Filters". See https://wiki.chatterino.com/Filters/ for how they work or how to configure them. (#1748, #2083, #2090, #2200, #2225)
- Major: Added Streamer Mode configuration (under `Settings -> General`), where you can select which features of Chatterino should behave differently when you are in Streamer Mode. (#2001, #2316, #2342, #2376) - Major: Added Streamer Mode configuration (under `Settings -> General`), where you can select which features of Chatterino should behave differently when you are in Streamer Mode. (#2001, #2316, #2342, #2376)
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284) - Major: Add `/settitle` and `/setgame` commands, originally made for Mm2PL/Dankerino. (#2534, #2609)
- Major: Color mentions to match the mentioned users. You can disable this by unchecking "Color @usernames" under `Settings -> General -> Advanced (misc.)`. (#1963, #2284, #2597)
- Major: Commands `/ignore` and `/unignore` have been renamed to `/block` and `/unblock` in order to keep consistency with Twitch's terms. (#2370) - Major: Commands `/ignore` and `/unignore` have been renamed to `/block` and `/unblock` in order to keep consistency with Twitch's terms. (#2370)
- Major: Added support for bit emotes - the ones you unlock after cheering to streamer. (#2550) - Major: Added support for bit emotes - the ones you unlock after cheering to streamer. (#2550)
- Minor: Added `/clearmessages` command - does what "Burger menu -> More -> Clear messages" does. (#2485) - Minor: Added `/clearmessages` command - does what "Burger menu -> More -> Clear messages" does. (#2485)
@ -43,7 +44,7 @@
- Minor: Show channels live now enabled by default - Minor: Show channels live now enabled by default
- Minor: Bold usernames enabled by default - Minor: Bold usernames enabled by default
- Minor: Improve UX of the "Login expired!" message (#2029) - Minor: Improve UX of the "Login expired!" message (#2029)
- Minor: PageUp and PageDown now scroll in the selected split (#2070, #2081) - Minor: PageUp and PageDown now scroll in the selected split and in the emote popup (#2070, #2081, #2410, #2607)
- Minor: Allow highlights to be excluded from `/mentions`. Excluded highlights will not trigger tab highlights either. (#1793, #2036) - Minor: Allow highlights to be excluded from `/mentions`. Excluded highlights will not trigger tab highlights either. (#1793, #2036)
- Minor: Flag all popup dialogs as actual dialogs so they get the relevant window manager hints (#1843, #2182, #2185, #2232, #2234) - Minor: Flag all popup dialogs as actual dialogs so they get the relevant window manager hints (#1843, #2182, #2185, #2232, #2234)
- Minor: Don't show update button for nightly builds on macOS and Linux, this was already the case for Windows (#2163, #2164) - Minor: Don't show update button for nightly builds on macOS and Linux, this was already the case for Windows (#2163, #2164)
@ -59,6 +60,7 @@
- Minor: Added `/streamlink` command. Usage: `/streamlink <channel>`. You can also use the command without arguments in any twitch channel to open it in streamlink. (#2443, #2495) - Minor: Added `/streamlink` command. Usage: `/streamlink <channel>`. You can also use the command without arguments in any twitch channel to open it in streamlink. (#2443, #2495)
- Minor: Humanized all numbers visible to end-users. (#2488) - Minor: Humanized all numbers visible to end-users. (#2488)
- Minor: Added a context menu to avatar in usercard. It opens on right-clicking the avatar in usercard. (#2517) - Minor: Added a context menu to avatar in usercard. It opens on right-clicking the avatar in usercard. (#2517)
- Minor: Handle messages that users can share after unlocking a new bits badge. (#2611)
- Bugfix: Fix crash occurring when pressing Escape in the Color Picker Dialog (#1843) - Bugfix: Fix crash occurring when pressing Escape in the Color Picker Dialog (#1843)
- Bugfix: Fix bug where the "check user follow state" event could trigger a network request requesting the user to follow or unfollow a user. By itself its quite harmless as it just repeats to Twitch the same follow state we had, so no follows should have been lost by this but it meant there was a rogue network request that was fired that could cause a crash (#1906) - Bugfix: Fix bug where the "check user follow state" event could trigger a network request requesting the user to follow or unfollow a user. By itself its quite harmless as it just repeats to Twitch the same follow state we had, so no follows should have been lost by this but it meant there was a rogue network request that was fired that could cause a crash (#1906)
- Bugfix: /usercard command will now respect the "Automatically close user popup" setting (#1918) - Bugfix: /usercard command will now respect the "Automatically close user popup" setting (#1918)
@ -81,11 +83,14 @@
- Bugfix: Fix anonymous users being pinged by "username" justinfan64537 (#2156, #2352) - Bugfix: Fix anonymous users being pinged by "username" justinfan64537 (#2156, #2352)
- Bugfix: Fixed hidden tooltips when always on top is active (#2384) - Bugfix: Fixed hidden tooltips when always on top is active (#2384)
- Bugfix: Fix CLI arguments (`--help`, `--version`, `--channels`) not being respected (#2368, #2190) - Bugfix: Fix CLI arguments (`--help`, `--version`, `--channels`) not being respected (#2368, #2190)
- Bugfix: Fixed search field not being focused on popup open (#2540)
- Bugfix: Fix Twitch cheer emotes not displaying tooltips when hovered (#2434, #2503) - Bugfix: Fix Twitch cheer emotes not displaying tooltips when hovered (#2434, #2503)
- Bugfix: Fix BTTV/FFZ channel emotes saying unknown error when no emotes found (#2542) - Bugfix: Fix BTTV/FFZ channel emotes saying unknown error when no emotes found (#2542)
- Bugfix: Fix directory not opening when clicking "Open AppData Directory" setting button on macOS (#2531, #2537) - Bugfix: Fix directory not opening when clicking "Open AppData Directory" setting button on macOS (#2531, #2537)
- Bugfix: Fix quickswitcher not respecting order of tabs when filtering (#2519, #2561) - Bugfix: Fix quickswitcher not respecting order of tabs when filtering (#2519, #2561)
- Bugfix: Fix GNOME not associating Chatterino's window with its desktop entry (#1863, #2587) - Bugfix: Fix GNOME not associating Chatterino's window with its desktop entry (#1863, #2587)
- Bugfix: Fix buffer overflow in emoji parsing. (#2602)
- Bugfix: Fix windows being brought back to life after the settings dialog was closed. (#1892, #2613)
- Dev: Updated minimum required Qt framework version to 5.12. (#2210) - Dev: Updated minimum required Qt framework version to 5.12. (#2210)
- Dev: Migrated `Kraken::getUser` to Helix (#2260) - Dev: Migrated `Kraken::getUser` to Helix (#2260)
- Dev: Migrated `TwitchAccount::(un)followUser` from Kraken to Helix and moved it to `Helix::(un)followUser`. (#2306) - Dev: Migrated `TwitchAccount::(un)followUser` from Kraken to Helix and moved it to `Helix::(un)followUser`. (#2306)

View file

@ -4,6 +4,7 @@ include(FeatureSummary)
list(APPEND CMAKE_MODULE_PATH list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_SOURCE_DIR}/cmake"
"${CMAKE_SOURCE_DIR}/cmake/sanitizers-cmake/cmake"
) )
project(chatterino VERSION 2.2.3) project(chatterino VERSION 2.2.3)
@ -44,6 +45,8 @@ if (WIN32)
find_package(WinToast REQUIRED) find_package(WinToast REQUIRED)
endif () endif ()
find_package(Sanitizers)
# Find boost on the system # Find boost on the system
find_package(Boost REQUIRED) find_package(Boost REQUIRED)

@ -0,0 +1 @@
Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b

View file

@ -13,8 +13,7 @@
</summary> </summary>
<description> <description>
<p> <p>
Chatterino 2 is the second installment of the Twitch chat client series Chatterino is a chat client for Twitch chat. It aims to be an improved/extended version of the Twitch web chat.
"Chatterino".
</p> </p>
</description> </description>
<screenshots> <screenshots>

View file

@ -13,7 +13,7 @@ AB_SETTINGS_CLASS *AB_SETTINGS_CLASS::instance = nullptr;
void _actuallyRegisterSetting( void _actuallyRegisterSetting(
std::weak_ptr<pajlada::Settings::SettingData> setting) std::weak_ptr<pajlada::Settings::SettingData> setting)
{ {
_settings.push_back(setting); _settings.push_back(std::move(setting));
} }
AB_SETTINGS_CLASS::AB_SETTINGS_CLASS(const QString &settingsDirectory) AB_SETTINGS_CLASS::AB_SETTINGS_CLASS(const QString &settingsDirectory)

View file

@ -460,6 +460,7 @@ endif ()
source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${SOURCE_FILES}) source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${SOURCE_FILES})
add_executable(${PROJECT_NAME} ${SOURCE_FILES}) add_executable(${PROJECT_NAME} ${SOURCE_FILES})
add_sanitizers(${PROJECT_NAME})
target_precompile_headers(${PROJECT_NAME} PRIVATE PrecompiledHeader.hpp) target_precompile_headers(${PROJECT_NAME} PRIVATE PrecompiledHeader.hpp)

View file

@ -106,7 +106,7 @@ void Args::applyCustomChannelLayout(const QString &argValue)
return QRect(-1, -1, -1, -1); return QRect(-1, -1, -1, -1);
}(); }();
window.geometry_ = std::move(configMainLayout); window.geometry_ = configMainLayout;
QStringList channelArgList = argValue.split(";"); QStringList channelArgList = argValue.split(";");
for (const QString &channelArg : channelArgList) for (const QString &channelArg : channelArgList)

View file

@ -320,13 +320,13 @@ void Channel::onConnected()
// Indirect channel // Indirect channel
// //
IndirectChannel::Data::Data(ChannelPtr _channel, Channel::Type _type) IndirectChannel::Data::Data(ChannelPtr _channel, Channel::Type _type)
: channel(_channel) : channel(std::move(_channel))
, type(_type) , type(_type)
{ {
} }
IndirectChannel::IndirectChannel(ChannelPtr channel, Channel::Type type) IndirectChannel::IndirectChannel(ChannelPtr channel, Channel::Type type)
: data_(std::make_unique<Data>(channel, type)) : data_(std::make_unique<Data>(std::move(channel), type))
{ {
} }
@ -339,7 +339,7 @@ void IndirectChannel::reset(ChannelPtr channel)
{ {
assert(this->data_->type != Channel::Type::Direct); assert(this->data_->type != Channel::Type::Direct);
this->data_->channel = channel; this->data_->channel = std::move(channel);
this->data_->changed.invoke(); this->data_->changed.invoke();
} }

View file

@ -6,7 +6,7 @@ namespace chatterino {
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting) void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting)
{ {
_actuallyRegisterSetting(setting); _actuallyRegisterSetting(std::move(setting));
} }
} // namespace chatterino } // namespace chatterino

View file

@ -19,6 +19,7 @@ enum class NetworkRequestType {
Post, Post,
Put, Put,
Delete, Delete,
Patch,
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -116,6 +116,19 @@ void loadUncached(const std::shared_ptr<NetworkData> &data)
return NetworkManager::accessManager.post( return NetworkManager::accessManager.post(
data->request_, data->payload_); data->request_, data->payload_);
} }
case NetworkRequestType::Patch:
if (data->multiPartPayload_)
{
assert(data->payload_.isNull());
return NetworkManager::accessManager.sendCustomRequest(
data->request_, "PATCH", data->multiPartPayload_);
}
else
{
return NetworkManager::accessManager.sendCustomRequest(
data->request_, "PATCH", data->payload_);
}
} }
return nullptr; return nullptr;
}(); }();

View file

@ -643,7 +643,85 @@ void CommandController::initialize(Settings &, Paths &paths)
getApp()->windows->getMainWindow().getNotebook().getSelectedPage()); getApp()->windows->getMainWindow().getNotebook().getSelectedPage());
currentPage->getSelectedSplit()->getChannelView().clearMessages(); currentPage->getSelectedSplit()->getChannelView().clearMessages();
return "";
});
this->registerCommand("/settitle", [](const QStringList &words,
ChannelPtr channel) {
if (words.size() < 2)
{
channel->addMessage(
makeSystemMessage("Usage: /settitle <stream title>."));
return "";
}
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
auto status = twitchChannel->accessStreamStatus();
auto title = words.mid(1).join(" ");
getHelix()->updateChannel(
twitchChannel->roomId(), "", "", title,
[channel, title](NetworkResult) {
channel->addMessage(makeSystemMessage(
QString("Updated title to %1").arg(title)));
},
[channel] {
channel->addMessage(
makeSystemMessage("Title update failed! Are you "
"missing the required scope?"));
});
}
else
{
channel->addMessage(makeSystemMessage(
"Unable to set title of non-Twitch channel."));
}
return "";
});
this->registerCommand("/setgame", [](const QStringList &words,
ChannelPtr channel) {
if (words.size() < 2)
{
channel->addMessage(
makeSystemMessage("Usage: /setgame <stream game>."));
return "";
}
if (auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get()))
{
getHelix()->searchGames(
words.mid(1).join(" "),
[channel, twitchChannel](std::vector<HelixGame> games) {
if (games.empty())
{
channel->addMessage(
makeSystemMessage("Game not found."));
}
else // 1 or more games
{
auto status = twitchChannel->accessStreamStatus();
getHelix()->updateChannel(
twitchChannel->roomId(), games.at(0).id, "", "",
[channel, games](NetworkResult) {
channel->addMessage(makeSystemMessage(
QString("Updated game to %1")
.arg(games.at(0).name)));
},
[channel] {
channel->addMessage(makeSystemMessage(
"Game update failed! Are you "
"missing the required scope?"));
});
}
},
[channel] {
channel->addMessage(
makeSystemMessage("Failed to look up game."));
});
}
else
{
channel->addMessage(
makeSystemMessage("Unable to set game of non-Twitch channel."));
}
return ""; return "";
}); });
} }

View file

@ -57,7 +57,7 @@ HighlightPhrase::HighlightPhrase(const QString &pattern, bool showInMentions,
, isRegex_(isRegex) , isRegex_(isRegex)
, isCaseSensitive_(isCaseSensitive) , isCaseSensitive_(isCaseSensitive)
, soundUrl_(soundUrl) , soundUrl_(soundUrl)
, color_(color) , color_(std::move(color))
, regex_(isRegex_ , regex_(isRegex_
? pattern ? pattern
: REGEX_START_BOUNDARY + QRegularExpression::escape(pattern) + : REGEX_START_BOUNDARY + QRegularExpression::escape(pattern) +

View file

@ -39,7 +39,7 @@ namespace {
} // namespace } // namespace
MessageLayout::MessageLayout(MessagePtr message) MessageLayout::MessageLayout(MessagePtr message)
: message_(message) : message_(std::move(message))
, container_(std::make_shared<MessageLayoutContainer>()) , container_(std::make_shared<MessageLayoutContainer>())
{ {
DebugCount::increase("message layout"); DebugCount::increase("message layout");

View file

@ -88,7 +88,7 @@ FlagsEnum<MessageElementFlag> MessageLayoutElement::getFlags() const
ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image, ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image,
const QSize &size) const QSize &size)
: MessageLayoutElement(creator, size) : MessageLayoutElement(creator, size)
, image_(image) , image_(std::move(image))
{ {
this->trailingSpace = creator.hasTrailingSpace(); this->trailingSpace = creator.hasTrailingSpace();
} }

View file

@ -26,7 +26,7 @@ const std::shared_ptr<QColor> ColorProvider::color(ColorType type) const
void ColorProvider::updateColor(ColorType type, QColor color) void ColorProvider::updateColor(ColorType type, QColor color)
{ {
auto colorPtr = this->typeColorMap_.at(type); auto colorPtr = this->typeColorMap_.at(type);
*colorPtr = color; *colorPtr = std::move(color);
} }
QSet<QColor> ColorProvider::recentColors() const QSet<QColor> ColorProvider::recentColors() const

View file

@ -24,7 +24,7 @@ namespace {
const rapidjson::Value &unparsedEmoji, const rapidjson::Value &unparsedEmoji,
QString shortCode = QString()) QString shortCode = QString())
{ {
static uint unicodeBytes[4]; std::array<uint32_t, 7> unicodeBytes;
struct { struct {
bool apple; bool apple;
@ -91,11 +91,12 @@ namespace {
for (const QString &unicodeCharacter : unicodeCharacters) for (const QString &unicodeCharacter : unicodeCharacters)
{ {
unicodeBytes[numUnicodeBytes++] = unicodeBytes.at(numUnicodeBytes++) =
QString(unicodeCharacter).toUInt(nullptr, 16); QString(unicodeCharacter).toUInt(nullptr, 16);
} }
emojiData->value = QString::fromUcs4(unicodeBytes, numUnicodeBytes); emojiData->value =
QString::fromUcs4(unicodeBytes.data(), numUnicodeBytes);
} }
// getToneNames takes a tones and returns their names in the same order // getToneNames takes a tones and returns their names in the same order

View file

@ -14,6 +14,7 @@
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/FormatTime.hpp" #include "util/FormatTime.hpp"
#include "util/Helpers.hpp"
#include "util/IrcHelpers.hpp" #include "util/IrcHelpers.hpp"
#include <IrcMessage> #include <IrcMessage>
@ -579,10 +580,11 @@ std::vector<MessagePtr> IrcMessageHandler::parseUserNoticeMessage(
content = parameters[1]; content = parameters[1];
} }
if (msgType == "sub" || msgType == "resub" || msgType == "subgift") if (msgType == "sub" || msgType == "resub" || msgType == "subgift" ||
msgType == "bitsbadgetier")
{ {
// Sub-specific message. I think it's only allowed for "resub" messages // Sub-specific and bits badge upgrade specific message.
// atm // It's only allowed for "resub" messages.
if (!content.isEmpty()) if (!content.isEmpty())
{ {
MessageParseArgs args; MessageParseArgs args;
@ -600,8 +602,17 @@ std::vector<MessagePtr> IrcMessageHandler::parseUserNoticeMessage(
if (it != tags.end()) if (it != tags.end())
{ {
auto b = QString messageText = it.value().toString();
MessageBuilder(systemMessage, parseTagString(it.value().toString()),
if (msgType == "bitsbadgetier")
{
messageText = QString("%1 just earned a new %2 Bits badge!")
.arg(tags.value("display-name").toString())
.arg(kFormatNumbers(
tags.value("msg-param-threshold").toInt()));
}
auto b = MessageBuilder(systemMessage, parseTagString(messageText),
calculateMessageTimestamp(message)); calculateMessageTimestamp(message));
b->flags.set(MessageFlag::Subscription); b->flags.set(MessageFlag::Subscription);
@ -628,10 +639,11 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
content = parameters[1]; content = parameters[1];
} }
if (msgType == "sub" || msgType == "resub" || msgType == "subgift") if (msgType == "sub" || msgType == "resub" || msgType == "subgift" ||
msgType == "bitsbadgetier")
{ {
// Sub-specific message. I think it's only allowed for "resub" messages // Sub-specific and bits badge upgrade specific message.
// atm // It's only allowed for "resub" messages.
if (!content.isEmpty()) if (!content.isEmpty())
{ {
this->addMessage(message, target, content, server, true, false); this->addMessage(message, target, content, server, true, false);
@ -642,8 +654,17 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
if (it != tags.end()) if (it != tags.end())
{ {
auto b = QString messageText = it.value().toString();
MessageBuilder(systemMessage, parseTagString(it.value().toString()),
if (msgType == "bitsbadgetier")
{
messageText = QString("%1 just earned a new %2 Bits badge!")
.arg(tags.value("display-name").toString())
.arg(kFormatNumbers(
tags.value("msg-param-threshold").toInt()));
}
auto b = MessageBuilder(systemMessage, parseTagString(messageText),
calculateMessageTimestamp(message)); calculateMessageTimestamp(message));
b->flags.set(MessageFlag::Subscription); b->flags.set(MessageFlag::Subscription);

View file

@ -64,7 +64,7 @@ QColor TwitchAccount::color()
void TwitchAccount::setColor(QColor color) void TwitchAccount::setColor(QColor color)
{ {
this->color_.set(color); this->color_.set(std::move(color));
} }
bool TwitchAccount::setOAuthClient(const QString &newClientID) bool TwitchAccount::setOAuthClient(const QString &newClientID)
@ -131,7 +131,7 @@ void TwitchAccount::blockUser(QString userId, std::function<void()> onSuccess,
} }
onSuccess(); onSuccess();
}, },
onFailure); std::move(onFailure));
} }
void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess, void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
@ -149,7 +149,7 @@ void TwitchAccount::unblockUser(QString userId, std::function<void()> onSuccess,
} }
onSuccess(); onSuccess();
}, },
onFailure); std::move(onFailure));
} }
void TwitchAccount::checkFollow(const QString targetUserID, void TwitchAccount::checkFollow(const QString targetUserID,

View file

@ -484,6 +484,7 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
if (match.hasMatch()) if (match.hasMatch())
{ {
QString username = match.captured(1); QString username = match.captured(1);
auto originalTextColor = textColor;
if (this->twitchChannel != nullptr && getSettings()->colorUsernames) if (this->twitchChannel != nullptr && getSettings()->colorUsernames)
{ {
@ -495,13 +496,23 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
} }
} }
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername, auto prefixedUsername = '@' + username;
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::BoldUsername,
textColor, FontStyle::ChatMediumBold) textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username}); ->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(prefixedUsername,
MessageElementFlag::NonBoldUsername,
textColor)
->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(prefixedUsername),
MessageElementFlag::Text,
originalTextColor);
this->emplace<TextElement>(
string, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username});
return; return;
} }
} }
@ -514,6 +525,8 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
if (match.hasMatch() && chatters->contains(username)) if (match.hasMatch() && chatters->contains(username))
{ {
auto originalTextColor = textColor;
if (getSettings()->colorUsernames) if (getSettings()->colorUsernames)
{ {
if (auto userColor = if (auto userColor =
@ -524,13 +537,21 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
} }
} }
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername, this->emplace<TextElement>(username,
MessageElementFlag::BoldUsername,
textColor, FontStyle::ChatMediumBold) textColor, FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, username}); ->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>( this->emplace<TextElement>(
string, MessageElementFlag::NonBoldUsername, textColor) username, MessageElementFlag::NonBoldUsername, textColor)
->setLink({Link::UserInfo, username}); ->setLink({Link::UserInfo, username})
->setTrailingSpace(false);
this->emplace<TextElement>(string.remove(username),
MessageElementFlag::Text,
originalTextColor);
return; return;
} }
} }

View file

@ -58,7 +58,7 @@ void Helix::getUserByName(QString userName,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
QStringList userIds; QStringList userIds;
QStringList userLogins{userName}; QStringList userLogins{std::move(userName)};
this->fetchUsers( this->fetchUsers(
userIds, userLogins, userIds, userLogins,
@ -78,7 +78,7 @@ void Helix::getUserById(QString userId,
ResultCallback<HelixUser> successCallback, ResultCallback<HelixUser> successCallback,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
QStringList userIds{userId}; QStringList userIds{std::move(userId)};
QStringList userLogins; QStringList userLogins;
this->fetchUsers( this->fetchUsers(
@ -136,7 +136,8 @@ void Helix::getUserFollowers(
QString userId, ResultCallback<HelixUsersFollowsResponse> successCallback, QString userId, ResultCallback<HelixUsersFollowsResponse> successCallback,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
this->fetchUsersFollows("", userId, successCallback, failureCallback); this->fetchUsersFollows("", std::move(userId), std::move(successCallback),
std::move(failureCallback));
} }
void Helix::getUserFollow( void Helix::getUserFollow(
@ -145,7 +146,7 @@ void Helix::getUserFollow(
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
this->fetchUsersFollows( this->fetchUsersFollows(
userId, targetId, std::move(userId), std::move(targetId),
[successCallback](const auto &response) { [successCallback](const auto &response) {
if (response.data.empty()) if (response.data.empty())
{ {
@ -155,7 +156,7 @@ void Helix::getUserFollow(
successCallback(true, response.data[0]); successCallback(true, response.data[0]);
}, },
failureCallback); std::move(failureCallback));
} }
void Helix::fetchStreams( void Helix::fetchStreams(
@ -209,7 +210,7 @@ void Helix::getStreamById(QString userId,
ResultCallback<bool, HelixStream> successCallback, ResultCallback<bool, HelixStream> successCallback,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
QStringList userIds{userId}; QStringList userIds{std::move(userId)};
QStringList userLogins; QStringList userLogins;
this->fetchStreams( this->fetchStreams(
@ -230,7 +231,7 @@ void Helix::getStreamByName(QString userName,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
QStringList userIds; QStringList userIds;
QStringList userLogins{userName}; QStringList userLogins{std::move(userName)};
this->fetchStreams( this->fetchStreams(
userIds, userLogins, userIds, userLogins,
@ -295,11 +296,47 @@ void Helix::fetchGames(QStringList gameIds, QStringList gameNames,
.execute(); .execute();
} }
void Helix::searchGames(QString gameName,
ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;
urlQuery.addQueryItem("query", gameName);
this->makeRequest("search/categories", urlQuery)
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
auto root = result.parseJson();
auto data = root.value("data");
if (!data.isArray())
{
failureCallback();
return Failure;
}
std::vector<HelixGame> games;
for (const auto &jsonStream : data.toArray())
{
games.emplace_back(jsonStream.toObject());
}
successCallback(games);
return Success;
})
.onError([failureCallback](auto /*result*/) {
// TODO: make better xd
failureCallback();
})
.execute();
}
void Helix::getGameById(QString gameId, void Helix::getGameById(QString gameId,
ResultCallback<HelixGame> successCallback, ResultCallback<HelixGame> successCallback,
HelixFailureCallback failureCallback) HelixFailureCallback failureCallback)
{ {
QStringList gameIds{gameId}; QStringList gameIds{std::move(gameId)};
QStringList gameNames; QStringList gameNames;
this->fetchGames( this->fetchGames(
@ -409,7 +446,7 @@ void Helix::createClip(QString channelId,
break; break;
} }
}) })
.finally(finallyCallback) .finally(std::move(finallyCallback))
.execute(); .execute();
} }
@ -578,6 +615,48 @@ void Helix::unblockUser(QString targetUserId,
.execute(); .execute();
} }
void Helix::updateChannel(QString broadcasterId, QString gameId,
QString language, QString title,
std::function<void(NetworkResult)> successCallback,
HelixFailureCallback failureCallback)
{
QUrlQuery urlQuery;
auto data = QJsonDocument();
auto obj = QJsonObject();
if (!gameId.isEmpty())
{
obj.insert("game_id", gameId);
}
if (!language.isEmpty())
{
obj.insert("broadcaster_language", language);
}
if (!title.isEmpty())
{
obj.insert("title", title);
}
if (title.isEmpty() && gameId.isEmpty() && language.isEmpty())
{
qCDebug(chatterinoCommon) << "Tried to update channel with no changes!";
return;
}
data.setObject(obj);
urlQuery.addQueryItem("broadcaster_id", broadcasterId);
this->makeRequest("channels", urlQuery)
.type(NetworkRequestType::Patch)
.header("Content-Type", "application/json")
.payload(data.toJson())
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
successCallback(result);
return Success;
})
.onError([failureCallback](NetworkResult result) {
failureCallback();
})
.execute();
}
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery) NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
{ {
assert(!url.startsWith("/")); assert(!url.startsWith("/"));
@ -611,8 +690,8 @@ NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
void Helix::update(QString clientId, QString oauthToken) void Helix::update(QString clientId, QString oauthToken)
{ {
this->clientId = clientId; this->clientId = std::move(clientId);
this->oauthToken = oauthToken; this->oauthToken = std::move(oauthToken);
} }
void Helix::initialize() void Helix::initialize()

View file

@ -252,6 +252,11 @@ public:
ResultCallback<std::vector<HelixGame>> successCallback, ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback); HelixFailureCallback failureCallback);
// https://dev.twitch.tv/docs/api/reference#search-categories
void searchGames(QString gameName,
ResultCallback<std::vector<HelixGame>> successCallback,
HelixFailureCallback failureCallback);
void getGameById(QString gameId, ResultCallback<HelixGame> successCallback, void getGameById(QString gameId, ResultCallback<HelixGame> successCallback,
HelixFailureCallback failureCallback); HelixFailureCallback failureCallback);
@ -296,6 +301,12 @@ public:
std::function<void()> successCallback, std::function<void()> successCallback,
HelixFailureCallback failureCallback); HelixFailureCallback failureCallback);
// https://dev.twitch.tv/docs/api/reference#modify-channel-information
void updateChannel(QString broadcasterId, QString gameId, QString language,
QString title,
std::function<void(NetworkResult)> successCallback,
HelixFailureCallback failureCallback);
void update(QString clientId, QString oauthToken); void update(QString clientId, QString oauthToken);
static void initialize(); static void initialize();

View file

@ -63,8 +63,8 @@ NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery)
void Kraken::update(QString clientId, QString oauthToken) void Kraken::update(QString clientId, QString oauthToken)
{ {
this->clientId = clientId; this->clientId = std::move(clientId);
this->oauthToken = oauthToken; this->oauthToken = std::move(oauthToken);
} }
void Kraken::initialize() void Kraken::initialize()

View file

@ -104,6 +104,16 @@ URL: https://dev.twitch.tv/docs/api/reference#get-channel-information
Used in: Used in:
- `TwitchChannel` to refresh stream title - `TwitchChannel` to refresh stream title
### Update Channel
URL: https://dev.twitch.tv/docs/api/reference#modify-channel-information
Requires `channel:manage:broadcast` scope
- We implement this in `providers/twitch/api/Helix.cpp updateChannel`
Used in:
- `/setgame` to update the game in the current channel
- `/settitle` to update the title in the current channel
### Create Stream Marker ### Create Stream Marker
URL: https://dev.twitch.tv/docs/api/reference/#create-stream-marker URL: https://dev.twitch.tv/docs/api/reference/#create-stream-marker
@ -142,6 +152,14 @@ Requires `user:manage:blocked_users` scope
- `widgets/dialogs/UserInfoPopup.cpp` to unblock a user via checkbox in the usercard - `widgets/dialogs/UserInfoPopup.cpp` to unblock a user via checkbox in the usercard
- `controllers/commands/CommandController.cpp` to unblock a user via "/unblock" command - `controllers/commands/CommandController.cpp` to unblock a user via "/unblock" command
### Search Categories
URL: https://dev.twitch.tv/docs/api/reference#search-categories
- We implement this in `providers/twitch/api/Helix.cpp searchGames`
Used in:
- `controllers/commands/CommandController.cpp` in `/setgame` command to fuzzy search for game titles
## TMI ## TMI
The TMI api is undocumented. The TMI api is undocumented.

View file

@ -33,7 +33,7 @@ TooltipPreviewImage::TooltipPreviewImage()
void TooltipPreviewImage::setImage(ImagePtr image) void TooltipPreviewImage::setImage(ImagePtr image)
{ {
this->image_ = image; this->image_ = std::move(image);
this->refreshTooltipWidgetPixmap(); this->refreshTooltipWidgetPixmap();
} }

View file

@ -42,4 +42,9 @@ QString localizeNumbers(const int &number)
return locale.toString(number); return locale.toString(number);
} }
QString kFormatNumbers(const int &number)
{
return QString("%1K").arg(number / 1000);
}
} // namespace chatterino } // namespace chatterino

View file

@ -15,4 +15,6 @@ QString shortenString(const QString &str, unsigned maxWidth = 50);
QString localizeNumbers(const int &number); QString localizeNumbers(const int &number);
QString kFormatNumbers(const int &number);
} // namespace chatterino } // namespace chatterino

View file

@ -17,7 +17,7 @@ class LambdaRunnable : public QRunnable
public: public:
LambdaRunnable(std::function<void()> action) LambdaRunnable(std::function<void()> action)
{ {
this->action_ = action; this->action_ = std::move(action);
} }
void run() void run()

View file

@ -143,7 +143,7 @@ void AttachedWindow::detach(const QString &winId)
void AttachedWindow::setChannel(ChannelPtr channel) void AttachedWindow::setChannel(ChannelPtr channel)
{ {
this->ui_.split->setChannel(channel); this->ui_.split->setChannel(std::move(channel));
} }
void AttachedWindow::showEvent(QShowEvent *) void AttachedWindow::showEvent(QShowEvent *)

View file

@ -244,7 +244,10 @@ void BaseWindow::init()
getSettings()->windowTopMost.connect( getSettings()->windowTopMost.connect(
[this](bool topMost, auto) { [this](bool topMost, auto) {
this->setWindowFlag(Qt::WindowStaysOnTopHint, topMost); this->setWindowFlag(Qt::WindowStaysOnTopHint, topMost);
if (this->isVisible())
{
this->show(); this->show();
}
}, },
this->managedConnections_); this->managedConnections_);
} }

View file

@ -5,13 +5,13 @@
namespace chatterino { namespace chatterino {
Label::Label(QString text, FontStyle style) Label::Label(QString text, FontStyle style)
: Label(nullptr, text, style) : Label(nullptr, std::move(text), style)
{ {
} }
Label::Label(BaseWidget *parent, QString text, FontStyle style) Label::Label(BaseWidget *parent, QString text, FontStyle style)
: BaseWidget(parent) : BaseWidget(parent)
, text_(text) , text_(std::move(text))
, fontStyle_(style) , fontStyle_(style)
{ {
this->connections_.managedConnect(getFonts()->fontChanged, [this] { this->connections_.managedConnect(getFonts()->fontChanged, [this] {

View file

@ -26,7 +26,7 @@ StreamView::StreamView(ChannelPtr channel, const QUrl &url)
auto chat = layoutCreator.emplace<ChannelView>(); auto chat = layoutCreator.emplace<ChannelView>();
chat->setFixedWidth(300); chat->setFixedWidth(300);
chat->setChannel(channel); chat->setChannel(std::move(channel));
this->layout()->setSpacing(0); this->layout()->setSpacing(0);
this->layout()->setMargin(0); this->layout()->setMargin(0);

View file

@ -11,6 +11,7 @@
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/Shortcut.hpp" #include "util/Shortcut.hpp"
#include "widgets/Notebook.hpp" #include "widgets/Notebook.hpp"
#include "widgets/Scrollbar.hpp"
#include "widgets/helper/ChannelView.hpp" #include "widgets/helper/ChannelView.hpp"
#include <QHBoxLayout> #include <QHBoxLayout>
@ -178,6 +179,20 @@ EmotePopup::EmotePopup(QWidget *parent)
createWindowShortcut(this, "CTRL+Shift+Tab", [=] { createWindowShortcut(this, "CTRL+Shift+Tab", [=] {
notebook->selectPreviousTab(); notebook->selectPreviousTab();
}); });
// Scroll with Page Up / Page Down
createWindowShortcut(this, "PgUp", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(-scrollbar.getLargeChange());
});
createWindowShortcut(this, "PgDown", [=] {
auto &scrollbar =
dynamic_cast<ChannelView *>(notebook->getSelectedPage())
->getScrollBar();
scrollbar.offset(scrollbar.getLargeChange());
});
} }
void EmotePopup::loadChannel(ChannelPtr _channel) void EmotePopup::loadChannel(ChannelPtr _channel)

View file

@ -45,7 +45,7 @@ void NotificationPopup::updatePosition()
void NotificationPopup::addMessage(MessagePtr msg) void NotificationPopup::addMessage(MessagePtr msg)
{ {
this->channel_->addMessage(msg); this->channel_->addMessage(std::move(msg));
// QTimer::singleShot(5000, this, [this, msg] { this->channel->remove }); // QTimer::singleShot(5000, this, [this, msg] { this->channel->remove });
} }

View file

@ -38,7 +38,7 @@ Button::Button(BaseWidget *parent)
void Button::setMouseEffectColor(boost::optional<QColor> color) void Button::setMouseEffectColor(boost::optional<QColor> color)
{ {
this->mouseEffectColor_ = color; this->mouseEffectColor_ = std::move(color);
} }
void Button::setPixmap(const QPixmap &_pixmap) void Button::setPixmap(const QPixmap &_pixmap)

View file

@ -550,7 +550,7 @@ bool ChannelView::getEnableScrollingToBottom() const
void ChannelView::setOverrideFlags(boost::optional<MessageElementFlags> value) void ChannelView::setOverrideFlags(boost::optional<MessageElementFlags> value)
{ {
this->overrideFlags_ = value; this->overrideFlags_ = std::move(value);
} }
const boost::optional<MessageElementFlags> &ChannelView::getOverrideFlags() const boost::optional<MessageElementFlags> &ChannelView::getOverrideFlags()
@ -647,7 +647,7 @@ void ChannelView::setChannel(ChannelPtr underlyingChannel)
this->channelConnections_.push_back(this->channel_->messageAppended.connect( this->channelConnections_.push_back(this->channel_->messageAppended.connect(
[this](MessagePtr &message, [this](MessagePtr &message,
boost::optional<MessageFlags> overridingFlags) { boost::optional<MessageFlags> overridingFlags) {
this->messageAppended(message, overridingFlags); this->messageAppended(message, std::move(overridingFlags));
})); }));
this->channelConnections_.push_back( this->channelConnections_.push_back(
@ -753,7 +753,7 @@ ChannelPtr ChannelView::sourceChannel() const
void ChannelView::setSourceChannel(ChannelPtr sourceChannel) void ChannelView::setSourceChannel(ChannelPtr sourceChannel)
{ {
this->sourceChannel_ = sourceChannel; this->sourceChannel_ = std::move(sourceChannel);
} }
bool ChannelView::hasSourceChannel() const bool ChannelView::hasSourceChannel() const

View file

@ -66,7 +66,7 @@ SearchPopup::SearchPopup(QWidget *parent)
void SearchPopup::setChannelFilters(FilterSetPtr filters) void SearchPopup::setChannelFilters(FilterSetPtr filters)
{ {
this->channelFilters_ = filters; this->channelFilters_ = std::move(filters);
} }
void SearchPopup::setChannel(const ChannelPtr &channel) void SearchPopup::setChannel(const ChannelPtr &channel)
@ -155,6 +155,8 @@ void SearchPopup::initLayout()
this->setLayout(layout1); this->setLayout(layout1);
} }
this->searchInput_->setFocus();
} }
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates( std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(

View file

@ -45,6 +45,7 @@ add_executable(${PROJECT_NAME}
${chatterino_SOURCES} ${chatterino_SOURCES}
${test_SOURCES} ${test_SOURCES}
) )
add_sanitizers(${PROJECT_NAME})
# Enable autogeneration of Qts MOC/RCC/UIC # Enable autogeneration of Qts MOC/RCC/UIC
set_target_properties(${PROJECT_NAME} set_target_properties(${PROJECT_NAME}