make editing inline, add up arrow shortcut
This commit is contained in:
@@ -114,6 +114,10 @@
|
|||||||
border: 1px solid #026FB9;
|
border: 1px solid #026FB9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-input.editing {
|
||||||
|
border: 1px solid #b9026f;
|
||||||
|
}
|
||||||
|
|
||||||
.message-input.bad-input {
|
.message-input.bad-input {
|
||||||
border: 1px solid #dd3300;
|
border: 1px solid #dd3300;
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
#include "audio/manager.hpp"
|
#include "audio/manager.hpp"
|
||||||
#include "discord/discord.hpp"
|
#include "discord/discord.hpp"
|
||||||
#include "dialogs/token.hpp"
|
#include "dialogs/token.hpp"
|
||||||
#include "dialogs/editmessage.hpp"
|
|
||||||
#include "dialogs/confirm.hpp"
|
#include "dialogs/confirm.hpp"
|
||||||
#include "dialogs/setstatus.hpp"
|
#include "dialogs/setstatus.hpp"
|
||||||
#include "dialogs/friendpicker.hpp"
|
#include "dialogs/friendpicker.hpp"
|
||||||
@@ -948,19 +947,15 @@ void Abaddon::ActionChatInputSubmit(ChatSubmitParams data) {
|
|||||||
|
|
||||||
if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return;
|
if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return;
|
||||||
|
|
||||||
m_discord.SendChatMessage(data, NOOP_CALLBACK);
|
if (data.EditingID.IsValid()) {
|
||||||
|
m_discord.EditMessage(data.ChannelID, data.EditingID, data.Message);
|
||||||
|
} else {
|
||||||
|
m_discord.SendChatMessage(data, NOOP_CALLBACK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) {
|
void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) {
|
||||||
const auto msg = m_discord.GetMessage(id);
|
m_main_window->EditMessage(id);
|
||||||
if (!msg.has_value()) return;
|
|
||||||
EditMessageDialog dlg(*m_main_window);
|
|
||||||
dlg.SetContent(msg->Content);
|
|
||||||
auto response = dlg.run();
|
|
||||||
if (response == Gtk::RESPONSE_OK) {
|
|
||||||
auto new_content = dlg.GetContent();
|
|
||||||
m_discord.EditMessage(channel_id, id, new_content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Abaddon::ActionInsertMention(Snowflake id) {
|
void Abaddon::ActionInsertMention(Snowflake id) {
|
||||||
@@ -1156,6 +1151,7 @@ int main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
spdlog::cfg::load_env_levels();
|
spdlog::cfg::load_env_levels();
|
||||||
|
auto log_ui = spdlog::stdout_color_mt("ui");
|
||||||
auto log_audio = spdlog::stdout_color_mt("audio");
|
auto log_audio = spdlog::stdout_color_mt("audio");
|
||||||
auto log_voice = spdlog::stdout_color_mt("voice");
|
auto log_voice = spdlog::stdout_color_mt("voice");
|
||||||
auto log_discord = spdlog::stdout_color_mt("discord");
|
auto log_discord = spdlog::stdout_color_mt("discord");
|
||||||
|
@@ -565,6 +565,16 @@ void ChatInput::StopReplying() {
|
|||||||
m_input.Get().get_style_context()->remove_class("replying");
|
m_input.Get().get_style_context()->remove_class("replying");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatInput::StartEditing(const Message &message) {
|
||||||
|
m_input.Get().grab_focus();
|
||||||
|
m_input.Get().get_style_context()->add_class("editing");
|
||||||
|
GetBuffer()->set_text(message.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatInput::StopEditing() {
|
||||||
|
m_input.Get().get_style_context()->remove_class("editing");
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatInput::AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file) {
|
bool ChatInput::AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file) {
|
||||||
try {
|
try {
|
||||||
const auto read_stream = file->read();
|
const auto read_stream = file->read();
|
||||||
|
@@ -139,6 +139,9 @@ public:
|
|||||||
void StartReplying();
|
void StartReplying();
|
||||||
void StopReplying();
|
void StopReplying();
|
||||||
|
|
||||||
|
void StartEditing(const Message &message);
|
||||||
|
void StopEditing();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file);
|
bool AddFileAsImageAttachment(const Glib::RefPtr<Gio::File> &file);
|
||||||
bool CanAttachFiles();
|
bool CanAttachFiles();
|
||||||
|
@@ -253,6 +253,23 @@ void ChatList::ActuallyRemoveMessage(Snowflake id) {
|
|||||||
RemoveMessageAndHeader(it->second);
|
RemoveMessageAndHeader(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Snowflake> ChatList::GetLastSentMessage() {
|
||||||
|
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||||
|
const auto self_id = discord.GetUserData().ID;
|
||||||
|
|
||||||
|
std::map<Snowflake, Gtk::Widget *> ordered(m_id_to_widget.begin(), m_id_to_widget.end());
|
||||||
|
|
||||||
|
for (auto it = ordered.crbegin(); it != ordered.crend(); it++) {
|
||||||
|
const auto *widget = dynamic_cast<ChatMessageItemContainer*>(it->second);
|
||||||
|
if (widget == nullptr) continue;
|
||||||
|
const auto msg = discord.GetMessage(widget->ID);
|
||||||
|
if (!msg.has_value()) continue;
|
||||||
|
if (msg->Author.ID == self_id) return msg->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void ChatList::SetupMenu() {
|
void ChatList::SetupMenu() {
|
||||||
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
|
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
|
||||||
m_menu_copy_id->signal_activate().connect([this] {
|
m_menu_copy_id->signal_activate().connect([this] {
|
||||||
|
@@ -23,6 +23,7 @@ public:
|
|||||||
void SetSeparateAll(bool separate);
|
void SetSeparateAll(bool separate);
|
||||||
void SetUsePinnedMenu(); // i think i need a better way to do menus
|
void SetUsePinnedMenu(); // i think i need a better way to do menus
|
||||||
void ActuallyRemoveMessage(Snowflake id); // perhaps not the best method name
|
void ActuallyRemoveMessage(Snowflake id); // perhaps not the best method name
|
||||||
|
std::optional<Snowflake> GetLastSentMessage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupMenu();
|
void SetupMenu();
|
||||||
|
@@ -50,8 +50,8 @@ ChatWindow::ChatWindow() {
|
|||||||
|
|
||||||
m_input->signal_submit().connect(sigc::mem_fun(*this, &ChatWindow::OnInputSubmit));
|
m_input->signal_submit().connect(sigc::mem_fun(*this, &ChatWindow::OnInputSubmit));
|
||||||
m_input->signal_escape().connect([this]() {
|
m_input->signal_escape().connect([this]() {
|
||||||
if (m_is_replying)
|
if (m_is_replying) StopReplying();
|
||||||
StopReplying();
|
if (m_is_editing) StopEditing();
|
||||||
});
|
});
|
||||||
m_input->signal_key_press_event().connect(sigc::mem_fun(*this, &ChatWindow::OnKeyPressEvent), false);
|
m_input->signal_key_press_event().connect(sigc::mem_fun(*this, &ChatWindow::OnKeyPressEvent), false);
|
||||||
m_input->show();
|
m_input->show();
|
||||||
@@ -132,8 +132,8 @@ void ChatWindow::SetActiveChannel(Snowflake id) {
|
|||||||
m_input->SetActiveChannel(id);
|
m_input->SetActiveChannel(id);
|
||||||
m_input_indicator->SetActiveChannel(id);
|
m_input_indicator->SetActiveChannel(id);
|
||||||
m_rate_limit_indicator->SetActiveChannel(id);
|
m_rate_limit_indicator->SetActiveChannel(id);
|
||||||
if (m_is_replying)
|
if (m_is_replying) StopReplying();
|
||||||
StopReplying();
|
if (m_is_editing) StopEditing();
|
||||||
|
|
||||||
#ifdef WITH_LIBHANDY
|
#ifdef WITH_LIBHANDY
|
||||||
m_tab_switcher->ReplaceActiveTab(id);
|
m_tab_switcher->ReplaceActiveTab(id);
|
||||||
@@ -274,15 +274,31 @@ bool ChatWindow::OnInputSubmit(ChatSubmitParams data) {
|
|||||||
|
|
||||||
data.ChannelID = m_active_channel;
|
data.ChannelID = m_active_channel;
|
||||||
data.InReplyToID = m_replying_to;
|
data.InReplyToID = m_replying_to;
|
||||||
|
data.EditingID = m_editing_id;
|
||||||
|
|
||||||
if (m_active_channel.IsValid())
|
if (m_active_channel.IsValid())
|
||||||
m_signal_action_chat_submit.emit(data); // m_replying_to is checked for invalid in the handler
|
m_signal_action_chat_submit.emit(data); // m_replying_to is checked for invalid in the handler
|
||||||
if (m_is_replying)
|
|
||||||
StopReplying();
|
if (m_is_replying) StopReplying();
|
||||||
|
if (m_is_editing) StopEditing();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatWindow::ProcessKeyEvent(GdkEventKey *e) {
|
||||||
|
if (e->type != GDK_KEY_PRESS) return false;
|
||||||
|
if (e->keyval == GDK_KEY_Up) {
|
||||||
|
const auto edit_id = m_chat->GetLastSentMessage();
|
||||||
|
if (edit_id.has_value()) {
|
||||||
|
StartEditing(*edit_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatWindow::OnKeyPressEvent(GdkEventKey *e) {
|
bool ChatWindow::OnKeyPressEvent(GdkEventKey *e) {
|
||||||
if (m_completer.ProcessKeyPress(e))
|
if (m_completer.ProcessKeyPress(e))
|
||||||
return true;
|
return true;
|
||||||
@@ -290,6 +306,9 @@ bool ChatWindow::OnKeyPressEvent(GdkEventKey *e) {
|
|||||||
if (m_input->ProcessKeyPress(e))
|
if (m_input->ProcessKeyPress(e))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (ProcessKeyEvent(e))
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,10 +319,11 @@ void ChatWindow::StartReplying(Snowflake message_id) {
|
|||||||
m_replying_to = message_id;
|
m_replying_to = message_id;
|
||||||
m_is_replying = true;
|
m_is_replying = true;
|
||||||
m_input->StartReplying();
|
m_input->StartReplying();
|
||||||
if (author.has_value())
|
if (author.has_value()) {
|
||||||
m_input_indicator->SetCustomMarkup("Replying to " + author->GetUsernameEscapedBold());
|
m_input_indicator->SetCustomMarkup("Replying to " + author->GetUsernameEscapedBold());
|
||||||
else
|
} else {
|
||||||
m_input_indicator->SetCustomMarkup("Replying...");
|
m_input_indicator->SetCustomMarkup("Replying...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatWindow::StopReplying() {
|
void ChatWindow::StopReplying() {
|
||||||
@@ -313,6 +333,25 @@ void ChatWindow::StopReplying() {
|
|||||||
m_input_indicator->ClearCustom();
|
m_input_indicator->ClearCustom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatWindow::StartEditing(Snowflake message_id) {
|
||||||
|
const auto message = Abaddon::Get().GetDiscordClient().GetMessage(message_id);
|
||||||
|
if (!message.has_value()) {
|
||||||
|
spdlog::get("ui")->warn("ChatWindow::StartEditing message is nullopt");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_is_editing = true;
|
||||||
|
m_editing_id = message_id;
|
||||||
|
m_input->StartEditing(*message);
|
||||||
|
m_input_indicator->SetCustomMarkup("Editing...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWindow::StopEditing() {
|
||||||
|
m_is_editing = false;
|
||||||
|
m_editing_id = Snowflake::Invalid;
|
||||||
|
m_input->StopEditing();
|
||||||
|
m_input_indicator->ClearCustom();
|
||||||
|
}
|
||||||
|
|
||||||
void ChatWindow::OnScrollEdgeOvershot(Gtk::PositionType pos) {
|
void ChatWindow::OnScrollEdgeOvershot(Gtk::PositionType pos) {
|
||||||
if (pos == Gtk::POS_TOP)
|
if (pos == Gtk::POS_TOP)
|
||||||
m_signal_action_chat_load_history.emit(m_active_channel);
|
m_signal_action_chat_load_history.emit(m_active_channel);
|
||||||
|
@@ -37,6 +37,9 @@ public:
|
|||||||
void SetTopic(const std::string &text);
|
void SetTopic(const std::string &text);
|
||||||
void AddAttachment(const Glib::RefPtr<Gio::File> &file);
|
void AddAttachment(const Glib::RefPtr<Gio::File> &file);
|
||||||
|
|
||||||
|
void StartEditing(Snowflake message_id);
|
||||||
|
void StopEditing();
|
||||||
|
|
||||||
#ifdef WITH_LIBHANDY
|
#ifdef WITH_LIBHANDY
|
||||||
void OpenNewTab(Snowflake id);
|
void OpenNewTab(Snowflake id);
|
||||||
TabsState GetTabsState();
|
TabsState GetTabsState();
|
||||||
@@ -55,10 +58,14 @@ protected:
|
|||||||
void StartReplying(Snowflake message_id);
|
void StartReplying(Snowflake message_id);
|
||||||
void StopReplying();
|
void StopReplying();
|
||||||
|
|
||||||
|
bool m_is_editing = false;
|
||||||
|
Snowflake m_editing_id;
|
||||||
|
|
||||||
Snowflake m_active_channel;
|
Snowflake m_active_channel;
|
||||||
|
|
||||||
bool OnInputSubmit(ChatSubmitParams data);
|
bool OnInputSubmit(ChatSubmitParams data);
|
||||||
|
|
||||||
|
bool ProcessKeyEvent(GdkEventKey *e);
|
||||||
bool OnKeyPressEvent(GdkEventKey *e);
|
bool OnKeyPressEvent(GdkEventKey *e);
|
||||||
void OnScrollEdgeOvershot(Gtk::PositionType pos);
|
void OnScrollEdgeOvershot(Gtk::PositionType pos);
|
||||||
|
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
#include "editmessage.hpp"
|
|
||||||
|
|
||||||
EditMessageDialog::EditMessageDialog(Gtk::Window &parent)
|
|
||||||
: Gtk::Dialog("Edit Message", parent, true)
|
|
||||||
, m_layout(Gtk::ORIENTATION_VERTICAL)
|
|
||||||
, m_ok("OK")
|
|
||||||
, m_cancel("Cancel")
|
|
||||||
, m_bbox(Gtk::ORIENTATION_HORIZONTAL) {
|
|
||||||
set_default_size(300, 50);
|
|
||||||
get_style_context()->add_class("app-window");
|
|
||||||
get_style_context()->add_class("app-popup");
|
|
||||||
|
|
||||||
m_ok.signal_clicked().connect([&]() {
|
|
||||||
m_content = m_text.get_buffer()->get_text();
|
|
||||||
response(Gtk::RESPONSE_OK);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_cancel.signal_clicked().connect([&]() {
|
|
||||||
response(Gtk::RESPONSE_CANCEL);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_bbox.pack_start(m_ok, Gtk::PACK_SHRINK);
|
|
||||||
m_bbox.pack_start(m_cancel, Gtk::PACK_SHRINK);
|
|
||||||
m_bbox.set_layout(Gtk::BUTTONBOX_END);
|
|
||||||
|
|
||||||
m_text.set_hexpand(true);
|
|
||||||
|
|
||||||
m_scroll.set_hexpand(true);
|
|
||||||
m_scroll.set_vexpand(true);
|
|
||||||
m_scroll.add(m_text);
|
|
||||||
|
|
||||||
m_layout.add(m_scroll);
|
|
||||||
m_layout.add(m_bbox);
|
|
||||||
get_content_area()->add(m_layout);
|
|
||||||
|
|
||||||
show_all_children();
|
|
||||||
}
|
|
||||||
|
|
||||||
Glib::ustring EditMessageDialog::GetContent() {
|
|
||||||
return m_content;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditMessageDialog::SetContent(const Glib::ustring &str) {
|
|
||||||
m_text.get_buffer()->set_text(str);
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class EditMessageDialog : public Gtk::Dialog {
|
|
||||||
public:
|
|
||||||
EditMessageDialog(Gtk::Window &parent);
|
|
||||||
Glib::ustring GetContent();
|
|
||||||
void SetContent(const Glib::ustring &str);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Gtk::Box m_layout;
|
|
||||||
Gtk::Button m_ok;
|
|
||||||
Gtk::Button m_cancel;
|
|
||||||
Gtk::ButtonBox m_bbox;
|
|
||||||
Gtk::ScrolledWindow m_scroll;
|
|
||||||
Gtk::TextView m_text;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Glib::ustring m_content;
|
|
||||||
};
|
|
@@ -20,6 +20,7 @@ struct ChatSubmitParams {
|
|||||||
bool Silent = false;
|
bool Silent = false;
|
||||||
Snowflake ChannelID;
|
Snowflake ChannelID;
|
||||||
Snowflake InReplyToID;
|
Snowflake InReplyToID;
|
||||||
|
Snowflake EditingID;
|
||||||
Glib::ustring Message;
|
Glib::ustring Message;
|
||||||
std::vector<Attachment> Attachments;
|
std::vector<Attachment> Attachments;
|
||||||
};
|
};
|
||||||
|
@@ -168,6 +168,10 @@ void MainWindow::ToggleMenuVisibility() {
|
|||||||
m_menu_bar.set_visible(!m_menu_bar.get_visible());
|
m_menu_bar.set_visible(!m_menu_bar.get_visible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::EditMessage(Snowflake message_id) {
|
||||||
|
m_chat.StartEditing(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WITH_LIBHANDY
|
#ifdef WITH_LIBHANDY
|
||||||
void MainWindow::GoBack() {
|
void MainWindow::GoBack() {
|
||||||
m_chat.GoBack();
|
m_chat.GoBack();
|
||||||
|
@@ -29,6 +29,7 @@ public:
|
|||||||
void UpdateChatReactionRemove(Snowflake id, const Glib::ustring ¶m);
|
void UpdateChatReactionRemove(Snowflake id, const Glib::ustring ¶m);
|
||||||
void UpdateMenus();
|
void UpdateMenus();
|
||||||
void ToggleMenuVisibility();
|
void ToggleMenuVisibility();
|
||||||
|
void EditMessage(Snowflake message_id);
|
||||||
|
|
||||||
#ifdef WITH_LIBHANDY
|
#ifdef WITH_LIBHANDY
|
||||||
void GoBack();
|
void GoBack();
|
||||||
|
Reference in New Issue
Block a user