Readded chat box

This commit is contained in:
Yousef Khadadeh 2026-04-09 16:30:28 +03:00
parent 99c2657434
commit 60af55d4db
19 changed files with 307 additions and 249 deletions

View file

@ -355,12 +355,14 @@ void BookmarkManager::setup_list_gui() {
});
});
if(editingBookmark) {
text_label_centered(gui, editingBookmark->is_folder() ? "Edit Bookmark Folder" : "Edit Bookmark");
auto editingBookmarkLock = editingBookmark.lock();
if(editingBookmarkLock) {
text_label_centered(gui, editingBookmarkLock->is_folder() ? "Edit Bookmark Folder" : "Edit Bookmark");
input_text_field(gui, "input edit name", "Name", &nameToEdit, {
.onEdit = [&] {
if(editingBookmark) { // This can be set to null on the way here (close bookmark popup when textbox is selected)
editingBookmark->set_name(world.delayedUpdateObjectManager, nameToEdit);
auto editingBookmarkLock = editingBookmark.lock();
if(editingBookmarkLock) { // This can be set to null on the way here (close bookmark popup when textbox is selected)
editingBookmarkLock->set_name(world.delayedUpdateObjectManager, nameToEdit);
world.main.g.gui.set_to_layout();
}
}
@ -572,18 +574,19 @@ void BookmarkManager::editing_bookmark_check() {
WorldUndoManager::UndoObjectID undoID;
};
if(editingBookmark) {
const std::string& currentNameData = editingBookmark->get_name();
auto editingBookmarkLock = editingBookmark.lock();
if(editingBookmarkLock) {
const std::string& currentNameData = editingBookmarkLock->get_name();
if(currentNameData != editingBookmarkOldName.value())
world.undo.push(std::make_unique<EditBookmarkWorldUndoAction>(editingBookmarkOldName.value(), world.undo.get_undoid_from_netid(editingBookmark.get_net_id())));
}
if(selectedBookmarkIndices.size() == 1) {
editingBookmark = get_bookmark_from_obj_index(*selectedBookmarkIndices.begin());
editingBookmarkOldName = editingBookmark->get_name();
editingBookmarkOldName = editingBookmark.lock()->get_name();
}
else {
editingBookmark = NetworkingObjects::NetObjTemporaryPtr<BookmarkListItem>();
editingBookmark.reset();
editingBookmarkOldName = std::nullopt;
}
}

View file

@ -33,7 +33,7 @@ class BookmarkManager {
std::set<GUIStuff::TreeListingObjIndexList> selectedBookmarkIndices;
NetworkingObjects::NetObjTemporaryPtr<BookmarkListItem> editingBookmark;
NetworkingObjects::NetObjWeakPtr<BookmarkListItem> editingBookmark;
std::optional<std::string> editingBookmarkOldName;
void editing_bookmark_check();

View file

@ -391,12 +391,14 @@ void DrawingProgramLayerManagerGUI::setup_list_gui() {
});
});
if(editingLayer) {
text_label_centered(gui, editingLayer->is_folder() ? "Edit Layer Folder" : "Edit Layer");
auto editingLayerLock = editingLayer.lock();
if(editingLayerLock) {
text_label_centered(gui, editingLayerLock->is_folder() ? "Edit Layer Folder" : "Edit Layer");
input_text_field(gui, "input edit name", "Name", &nameToEdit, {
.onEdit = [&] {
if(editingLayer) { // This can be set to null on the way here (close layer popup when textbox is selected)
editingLayer->set_name(world.delayedUpdateObjectManager, nameToEdit);
auto editingLayerLock = editingLayer.lock();
if(editingLayerLock) { // This can be set to null on the way here (close layer popup when textbox is selected)
editingLayerLock->set_name(world.delayedUpdateObjectManager, nameToEdit);
world.main.g.gui.set_to_layout();
}
}
@ -404,9 +406,10 @@ void DrawingProgramLayerManagerGUI::setup_list_gui() {
slider_scalar_field(gui, "input alpha slider", "Alpha", &alphaValToEdit, 0.0f, 1.0f, {
.decimalPrecision = 2,
.onEdit = [&] {
if(editingLayer) {
editingLayer->set_name(world.delayedUpdateObjectManager, nameToEdit);
editingLayer->set_alpha(layerMan, alphaValToEdit);
auto editingLayerLock = editingLayer.lock();
if(editingLayerLock) {
editingLayerLock->set_name(world.delayedUpdateObjectManager, nameToEdit);
editingLayerLock->set_alpha(layerMan, alphaValToEdit);
}
}
});
@ -415,8 +418,9 @@ void DrawingProgramLayerManagerGUI::setup_list_gui() {
gui.element<DropDown<size_t>>("input blend mode", &blendModeValToEdit, get_blend_mode_useful_name_list(), DropdownOptions{
.width = 190.0f,
.onClick = [&] {
if(editingLayer)
editingLayer->set_blend_mode(layerMan, get_blend_mode_useful_list()[blendModeValToEdit]);
auto editingLayerLock = editingLayer.lock();
if(editingLayerLock)
editingLayerLock->set_blend_mode(layerMan, get_blend_mode_useful_list()[blendModeValToEdit]);
}
});
});
@ -603,19 +607,19 @@ void DrawingProgramLayerManagerGUI::editing_layer_check() {
WorldUndoManager::UndoObjectID undoID;
};
if(editingLayer) {
const DrawingProgramLayerListItemMetaInfo& currentMetaData = editingLayer->get_metainfo();
auto editingLayerLock = editingLayer.lock();
if(editingLayerLock) {
const DrawingProgramLayerListItemMetaInfo& currentMetaData = editingLayerLock->get_metainfo();
if(currentMetaData != editingLayerOldMetainfo.value())
world.undo.push(std::make_unique<EditLayerWorldUndoAction>(std::make_unique<DrawingProgramLayerListItemMetaInfo>(editingLayerOldMetainfo.value()), world.undo.get_undoid_from_netid(editingLayer.get_net_id())));
}
if(selectedLayerIndices.size() == 1) {
editingLayer = get_layer_from_obj_index(*selectedLayerIndices.begin());
editingLayerOldMetainfo = editingLayer->get_metainfo();
editingLayerOldMetainfo = editingLayer.lock()->get_metainfo();
}
else {
editingLayer = NetworkingObjects::NetObjTemporaryPtr<DrawingProgramLayerListItem>();
editingLayer.reset();
editingLayerOldMetainfo = std::nullopt;
}
}

View file

@ -29,7 +29,7 @@ class DrawingProgramLayerManagerGUI {
size_t blendModeValToEdit = 0;
std::set<GUIStuff::TreeListingObjIndexList> selectedLayerIndices;
NetworkingObjects::NetObjTemporaryPtr<DrawingProgramLayerListItem> editingLayer;
NetworkingObjects::NetObjWeakPtr<DrawingProgramLayerListItem> editingLayer;
std::optional<DrawingProgramLayerListItemMetaInfo> editingLayerOldMetainfo;

View file

@ -12,6 +12,7 @@ template <typename OptT> SelectableButton::Data selectable_button_options_to_dat
SelectableButton::Data toRet;
toRet.onClick = options.onClick;
toRet.isSelected = options.isSelected;
toRet.instantResponse = options.instantResponse;
toRet.drawType = options.drawType;
toRet.onClickButton = options.onClickButton;
return toRet;
@ -20,6 +21,7 @@ template <typename OptT> SelectableButton::Data selectable_button_options_to_dat
struct TextButtonOptions {
SelectableButton::DrawType drawType = SelectableButton::DrawType::FILLED;
bool isSelected = false;
bool instantResponse = false;
bool wide = false;
bool centered = true;
@ -30,6 +32,7 @@ struct TextButtonOptions {
struct SVGButtonOptions {
SelectableButton::DrawType drawType = SelectableButton::DrawType::FILLED;
bool isSelected = false;
bool instantResponse = false;
float size = BIG_BUTTON_SIZE;
std::function<void()> onClick;
@ -39,6 +42,7 @@ struct SVGButtonOptions {
struct FixedSizeColorButtonOptions {
SelectableButton::DrawType drawType = SelectableButton::DrawType::FILLED;
bool isSelected = false;
bool instantResponse = false;
bool hasAlpha = true;
float size = BIG_BUTTON_SIZE;

View file

@ -8,7 +8,6 @@ namespace GUIStuff { namespace ElementHelpers {
template <typename T, typename OptT> TextBoxData<T> textbox_options_to_data(const OptT& options) {
TextBoxData<T> toRet;
toRet.updateEveryEdit = options.updateEveryEdit;
toRet.onEnter = options.onEnter;
toRet.onEdit = options.onEdit;
toRet.immutable = options.immutable;
@ -18,7 +17,6 @@ template <typename T, typename OptT> TextBoxData<T> textbox_options_to_data(cons
}
struct TextBoxOptions {
bool updateEveryEdit = true;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -28,7 +26,6 @@ struct TextBoxOptions {
struct TextBoxScalarOptions {
int decimalPrecision = 0;
bool updateEveryEdit = true;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -38,7 +35,6 @@ struct TextBoxScalarOptions {
struct TextBoxScalarsOptions {
int decimalPrecision = 0;
bool updateEveryEdit = true;
bool immutable = false;
std::function<void(size_t)> onEnter;
std::function<void(size_t)> onEdit;
@ -48,7 +44,6 @@ struct TextBoxScalarsOptions {
struct TextBoxPathOptions {
std::filesystem::file_type fileTypeRestriction;
bool updateEveryEdit = true;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -58,7 +53,6 @@ struct TextBoxPathOptions {
struct TextBoxHexColorOptions {
bool hasAlpha = true;
bool updateEveryEdit = true;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -122,7 +116,6 @@ template <typename TContainer, typename T> void input_scalars_field(GUIManager&
for(size_t i = 0; i < elemCount; i++) {
gui.new_id(i, [&] { input_scalar<T>(gui, "field", &(*val)[i], minVal, maxVal, TextBoxScalarOptions {
.decimalPrecision = options.decimalPrecision,
.updateEveryEdit = options.updateEveryEdit,
.immutable = options.immutable,
.onEnter = [f = options.onEnter, i] { if(f) f(i); },
.onEdit = [f = options.onEdit, i] { if(f) f(i); },

View file

@ -6,6 +6,7 @@ namespace GUIStuff {
gui(initGUI) {}
void Element::update() {}
void Element::deselect() {}
void Element::input_paste_callback(const CustomEvents::PasteEventData& paste) { }
void Element::input_text_key_callback(const InputManager::KeyCallbackArgs& key) { }

View file

@ -23,6 +23,7 @@ namespace GUIStuff {
Element* parent = nullptr;
virtual void update();
virtual void deselect();
virtual void input_paste_callback(const CustomEvents::PasteEventData& paste);
virtual void input_text_key_callback(const InputManager::KeyCallbackArgs& key);

View file

@ -14,6 +14,7 @@ void SelectableButton::layout(const Clay_ElementId& id, const Data& d) {
SkColor4f backgroundColorHighlight;
SkColor4f backgroundColor;
instantResponse = d.instantResponse;
onClick = [&, this, d] {
if(d.onClickButton)
d.onClickButton(this);
@ -70,10 +71,16 @@ void SelectableButton::layout(const Clay_ElementId& id, const Data& d) {
void SelectableButton::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
bool oldIsHeld = isHeld;
isHeld = mouseHovering && button.button == InputManager::MouseButton::LEFT && button.down;
if(isHeld)
if(isHeld) {
if(instantResponse) {
std::cout << "INSTANT" << std::endl;
gui.set_post_callback_func(onClick);
}
gui.set_to_layout();
}
else if(mouseHovering && oldIsHeld && button.button == InputManager::MouseButton::LEFT && !button.down) {
gui.set_post_callback_func(onClick);
if(!instantResponse)
gui.set_post_callback_func(onClick);
gui.set_to_layout();
}
else if(mouseHovering != oldIsHovering || isHeld != oldIsHeld)

View file

@ -21,6 +21,7 @@ class SelectableButton : public Element {
struct Data {
DrawType drawType = DrawType::TRANSPARENT_ALL;
bool isSelected = false;
bool instantResponse = false;
std::function<void()> onClick;
std::function<void(SelectableButton*)> onClickButton;
std::function<void(const InnerContentCallbackParameters&)> innerContent;
@ -32,6 +33,7 @@ class SelectableButton : public Element {
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
private:
bool instantResponse = false;
bool isHeld = false;
bool oldIsHovering = false;
std::function<void()> onClick;

View file

@ -14,7 +14,6 @@ template <typename T> struct TextBoxData {
std::function<std::optional<T>(const std::string&)> fromStr;
std::function<std::string(const T&)> toStr;
bool singleLine = true;
bool updateEveryEdit = true;
bool immutable = false;
InputManager::TextInputProperties textInputProps;
std::function<void()> onEdit;
@ -38,7 +37,9 @@ template <typename T> class TextBox : public Element {
TextBox(GUIManager& gui);
void layout(const Clay_ElementId& id, const TextBoxData<T>& userInfo);
virtual void update();
void select();
virtual void update() override;
virtual void deselect() override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
virtual void input_paste_callback(const CustomEvents::PasteEventData& paste) override;
virtual void input_text_key_callback(const InputManager::KeyCallbackArgs& key) override;

View file

@ -71,6 +71,23 @@ template <typename T> void TextBox<T>::clay_draw(SkCanvas* canvas, UpdateInputDa
canvas->restore();
}
template <typename T> void TextBox<T>::select() {
if(!is_selected()) {
SCollision::AABB<float> rect = {(boundingBox.value().min + gui.io.windowPos) * gui.io.guiScaleMultiplier, (boundingBox.value().max + gui.io.windowPos) * gui.io.guiScaleMultiplier};
unsigned id = gui.io.input->set_text_box_front(rect, userInfo.textInputProps);
edit = TextEditData(id, textbox, cur);
}
}
template <typename T> void TextBox<T>::deselect() {
if(is_selected()) {
gui.io.input->remove_text_box(edit.value().textboxInputID);
if(userInfo.onDeselect) userInfo.onDeselect();
reset_textbox_text();
edit = std::nullopt;
}
}
template <typename T> bool TextBox<T>::is_selected() const {
return edit.has_value();
}
@ -101,7 +118,7 @@ template <typename T> void TextBox<T>::after_text_input_callback() {
if(userInfo.immutable)
cur->pos = cur->selectionBeginPos = cur->selectionEndPos = textbox->insert({0, 0}, userInfo.toStr(*userInfo.data));
else {
if(userInfo.updateEveryEdit && update_data() && userInfo.onEdit)
if(update_data() && userInfo.onEdit)
gui.set_post_callback_func_high_priority(userInfo.onEdit);
}
}
@ -114,37 +131,22 @@ template <typename T> void TextBox<T>::input_text_callback(const InputManager::T
}
template <typename T> void TextBox<T>::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
auto& io = gui.io;
isHeld = false;
if(button.button == InputManager::MouseButton::LEFT && boundingBox.has_value()) {
if(button.down) {
if(mouseHovering) {
if(!is_selected()) {
SCollision::AABB<float> rect = {(boundingBox.value().min + io.windowPos) * io.guiScaleMultiplier, (boundingBox.value().max + io.windowPos) * io.guiScaleMultiplier};
unsigned id = gui.io.input->set_text_box_front(rect, userInfo.textInputProps);
edit = TextEditData(id, textbox, cur);
select();
gui.set_post_callback_func_high_priority([&] {
if(userInfo.onSelect) userInfo.onSelect();
});
}
isHeld = true;
textbox->process_mouse_left_button(*cur, button.pos - boundingBox.value().min, button.clicks, isHeld, gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held);
}
else if(is_selected()) {
gui.io.input->remove_text_box(edit.value().textboxInputID);
gui.set_post_callback_func_high_priority([&] {
if(update_data())
if(userInfo.onEdit) userInfo.onEdit();
if(userInfo.onDeselect) userInfo.onDeselect();
});
reset_textbox_text();
edit = std::nullopt;
textbox->process_mouse_left_button(*cur, button.pos - boundingBox.value().min, button.clicks, true, gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held);
}
else if(is_selected())
deselect();
}
else {
textbox->process_mouse_left_button(*cur, button.pos - boundingBox.value().min, 0, isHeld, gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held);
textbox->process_mouse_left_button(*cur, button.pos - boundingBox.value().min, 0, false, gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held);
}
}
}

View file

@ -482,6 +482,11 @@ bool GUIManager::cursor_obstructed() const {
return cursorObstructed;
}
void GUIManager::deselect_all() {
for(ElementContainer* e : orderedElements)
e->elem->deselect();
}
void GUIManager::input_paste_callback(const CustomEvents::PasteEventData& paste) {
for(ElementContainer* e : orderedElements)
e->elem->input_paste_callback(paste);

View file

@ -43,8 +43,8 @@ class GUIManager {
template <typename ElementType, typename... Args> ElementType* element(const char* id, const Args&... a) {
push_id(id);
Element* oldParent = parentElement;
ElementType* elem = insert_element<ElementType>();
Element* oldParent = parentElement;
elem->parent = parentElement;
parentElement = elem;
@ -61,9 +61,8 @@ class GUIManager {
template <typename ElementType, typename... Args> ElementType* clipping_element(const char* id, const Args&... a) {
push_id(id);
Element* oldParent = parentElement;
ElementType* elem = insert_element<ElementType>();
Element* oldParent = parentElement;
elem->parent = parentElement;
parentElement = elem;
@ -83,11 +82,12 @@ class GUIManager {
clippingRegion = oldClippingRegion;
parentElement = oldParent;
pop_id();
return elem;
}
void deselect_all();
DefaultStringArena strArena;
void input_paste_callback(const CustomEvents::PasteEventData& paste);

View file

@ -332,18 +332,18 @@ const InputManager::KeyData& InputManager::key(KeyCode kCode) {
return keys[kCode];
}
void InputManager::set_key_down(const SDL_KeyboardEvent& e, KeyCode kCode) {
void InputManager::set_key_down(const SDL_KeyboardEvent& e, KeyCode kCode, bool acceptingTextInput) {
auto& k = keys[kCode];
k.held = true;
if(text.is_accepting_input())
if(acceptingTextInput)
main.input_text_key_callback(KeyCallbackArgs{.key = kCode, .down = e.down, .repeat = e.repeat});
else
main.input_key_callback(KeyCallbackArgs{.key = kCode, .down = e.down, .repeat = e.repeat});
}
void InputManager::set_key_up(const SDL_KeyboardEvent& e, KeyCode kCode) {
void InputManager::set_key_up(const SDL_KeyboardEvent& e, KeyCode kCode, bool acceptingTextInput) {
keys[kCode].held = false;
if(text.is_accepting_input())
if(acceptingTextInput)
main.input_text_key_callback(KeyCallbackArgs{.key = kCode, .down = e.down, .repeat = e.repeat});
// Send key up even if text input is happening
main.input_key_callback(KeyCallbackArgs{.key = kCode, .down = e.down, .repeat = e.repeat});
@ -852,76 +852,78 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
return;
}
bool acceptingTextInput = text.is_accepting_input();
switch(kPress) {
case SDLK_UP:
set_key_down(e, KEY_GENERIC_UP);
set_key_down(e, KEY_GENERIC_UP, acceptingTextInput);
break;
case SDLK_DOWN:
set_key_down(e, KEY_GENERIC_DOWN);
set_key_down(e, KEY_GENERIC_DOWN, acceptingTextInput);
break;
case SDLK_LEFT:
set_key_down(e, KEY_GENERIC_LEFT);
set_key_down(e, KEY_GENERIC_LEFT, acceptingTextInput);
break;
case SDLK_RIGHT:
set_key_down(e, KEY_GENERIC_RIGHT);
set_key_down(e, KEY_GENERIC_RIGHT, acceptingTextInput);
break;
case SDLK_BACKSPACE:
set_key_down(e, KEY_TEXT_BACKSPACE);
set_key_down(e, KEY_TEXT_BACKSPACE, acceptingTextInput);
break;
case SDLK_DELETE:
set_key_down(e, KEY_TEXT_DELETE);
set_key_down(e, KEY_TEXT_DELETE, acceptingTextInput);
break;
case SDLK_HOME:
set_key_down(e, KEY_TEXT_HOME);
set_key_down(e, KEY_TEXT_HOME, acceptingTextInput);
break;
case SDLK_END:
set_key_down(e, KEY_TEXT_END);
set_key_down(e, KEY_TEXT_END, acceptingTextInput);
break;
case SDLK_LSHIFT:
set_key_down(e, KEY_GENERIC_LSHIFT);
set_key_down(e, KEY_GENERIC_LSHIFT, acceptingTextInput);
break;
case SDLK_LALT:
set_key_down(e, KEY_GENERIC_LALT);
set_key_down(e, KEY_GENERIC_LALT, acceptingTextInput);
break;
case SDLK_LCTRL:
set_key_down(e, KEY_GENERIC_LCTRL);
set_key_down(e, KEY_GENERIC_LCTRL, acceptingTextInput);
break;
case SDLK_LMETA:
set_key_down(e, KEY_GENERIC_LMETA);
set_key_down(e, KEY_GENERIC_LMETA, acceptingTextInput);
break;
case SDLK_C:
// Use either Ctrl or Meta (command for Mac) keys. We do either instead of checking, since checking can get complicated on Emscripten
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_COPY);
set_key_down(e, KEY_TEXT_COPY, acceptingTextInput);
break;
case SDLK_X:
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_CUT);
set_key_down(e, KEY_TEXT_CUT, acceptingTextInput);
break;
case SDLK_V:
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_PASTE);
set_key_down(e, KEY_TEXT_PASTE, acceptingTextInput);
break;
case SDLK_A:
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_SELECTALL);
set_key_down(e, KEY_TEXT_SELECTALL, acceptingTextInput);
break;
case SDLK_Z:
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_UNDO);
set_key_down(e, KEY_TEXT_UNDO, acceptingTextInput);
break;
case SDLK_R:
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_REDO);
set_key_down(e, KEY_TEXT_REDO, acceptingTextInput);
break;
case SDLK_RETURN:
set_key_down(e, KEY_GENERIC_ENTER);
set_key_down(e, KEY_GENERIC_ENTER, acceptingTextInput);
break;
case SDLK_TAB:
set_key_down(e, KEY_TEXT_TAB);
set_key_down(e, KEY_TEXT_TAB, acceptingTextInput);
break;
case SDLK_ESCAPE:
set_key_down(e, KEY_GENERIC_ESCAPE);
set_key_down(e, KEY_GENERIC_ESCAPE, acceptingTextInput);
break;
default:
break;
@ -929,7 +931,7 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
auto f = keyAssignments.find({make_generic_key_mod(kMod), kPress});
if(f != keyAssignments.end())
set_key_down(e, f->second);
set_key_down(e, f->second, acceptingTextInput);
}
void InputManager::call_paste(CustomEvents::PasteEventDataType type, const InputManagerCallPasteInfo& info) {
@ -968,66 +970,68 @@ bool InputManager::ctrl_or_meta_held() {
void InputManager::backend_key_up_update(const SDL_KeyboardEvent& e) {
auto kPress = e.key;
bool acceptingTextInput = text.is_accepting_input();
switch(kPress) {
case SDLK_UP:
set_key_up(e, KEY_GENERIC_UP);
set_key_up(e, KEY_GENERIC_UP, acceptingTextInput);
break;
case SDLK_DOWN:
set_key_up(e, KEY_GENERIC_DOWN);
set_key_up(e, KEY_GENERIC_DOWN, acceptingTextInput);
break;
case SDLK_LEFT:
set_key_up(e, KEY_GENERIC_LEFT);
set_key_up(e, KEY_GENERIC_LEFT, acceptingTextInput);
break;
case SDLK_RIGHT:
set_key_up(e, KEY_GENERIC_RIGHT);
set_key_up(e, KEY_GENERIC_RIGHT, acceptingTextInput);
break;
case SDLK_BACKSPACE:
set_key_up(e, KEY_TEXT_BACKSPACE);
set_key_up(e, KEY_TEXT_BACKSPACE, acceptingTextInput);
break;
case SDLK_DELETE:
set_key_up(e, KEY_TEXT_DELETE);
set_key_up(e, KEY_TEXT_DELETE, acceptingTextInput);
break;
case SDLK_HOME:
set_key_up(e, KEY_TEXT_HOME);
set_key_up(e, KEY_TEXT_HOME, acceptingTextInput);
break;
case SDLK_END:
set_key_up(e, KEY_TEXT_END);
set_key_up(e, KEY_TEXT_END, acceptingTextInput);
break;
case SDLK_LSHIFT:
set_key_up(e, KEY_GENERIC_LSHIFT);
set_key_up(e, KEY_GENERIC_LSHIFT, acceptingTextInput);
break;
case SDLK_LALT:
set_key_up(e, KEY_GENERIC_LALT);
set_key_up(e, KEY_GENERIC_LALT, acceptingTextInput);
break;
case SDLK_LCTRL:
set_key_up(e, KEY_GENERIC_LCTRL);
set_key_up(e, KEY_GENERIC_LCTRL, acceptingTextInput);
break;
case SDLK_C:
set_key_up(e, KEY_TEXT_COPY);
set_key_up(e, KEY_TEXT_COPY, acceptingTextInput);
break;
case SDLK_X:
set_key_up(e, KEY_TEXT_CUT);
set_key_up(e, KEY_TEXT_CUT, acceptingTextInput);
break;
case SDLK_V:
set_key_up(e, KEY_TEXT_PASTE);
set_key_up(e, KEY_TEXT_PASTE, acceptingTextInput);
break;
case SDLK_A:
set_key_up(e, KEY_TEXT_SELECTALL);
set_key_up(e, KEY_TEXT_SELECTALL, acceptingTextInput);
break;
case SDLK_Z:
set_key_up(e, KEY_TEXT_UNDO);
set_key_up(e, KEY_TEXT_UNDO, acceptingTextInput);
break;
case SDLK_R:
set_key_up(e, KEY_TEXT_REDO);
set_key_up(e, KEY_TEXT_REDO, acceptingTextInput);
break;
case SDLK_RETURN:
set_key_up(e, KEY_GENERIC_ENTER);
set_key_up(e, KEY_GENERIC_ENTER, acceptingTextInput);
break;
case SDLK_TAB:
set_key_up(e, KEY_TEXT_TAB);
set_key_up(e, KEY_TEXT_TAB, acceptingTextInput);
break;
case SDLK_ESCAPE:
set_key_up(e, KEY_GENERIC_ESCAPE);
set_key_up(e, KEY_GENERIC_ESCAPE, acceptingTextInput);
break;
default:
break;
@ -1035,7 +1039,7 @@ void InputManager::backend_key_up_update(const SDL_KeyboardEvent& e) {
for(auto& p : keyAssignments)
if(p.first.y() == kPress) // Dont try matching modifiers when setting key to go up
set_key_up(e, p.second);
set_key_up(e, p.second, acceptingTextInput);
}
void InputManager::Mouse::set_pos(const Vector2f& newPos) {

View file

@ -252,8 +252,8 @@ struct InputManager {
bool ctrl_or_meta_held();
uint32_t make_generic_key_mod(SDL_Keymod m);
void set_key_up(const SDL_KeyboardEvent& e, KeyCode kCode);
void set_key_down(const SDL_KeyboardEvent& e, KeyCode kCode);
void set_key_up(const SDL_KeyboardEvent& e, KeyCode kCode, bool acceptingTextInput);
void set_key_down(const SDL_KeyboardEvent& e, KeyCode kCode, bool acceptingTextInput);
std::vector<Vector2f> get_multiple_finger_positions();
std::vector<Vector2f> get_multiple_finger_motions();
void touch_finger_do_mouse_down();

View file

@ -370,6 +370,11 @@ void MainProgram::input_key_callback(const InputManager::KeyCallbackArgs& key) {
toolbar.save_as_func();
break;
}
case InputManager::KEY_OPEN_CHAT: {
if(key.down && !key.repeat)
toolbar.open_chatbox();
break;
}
}
g.input_key_callback(key);
if(world)
@ -378,6 +383,13 @@ void MainProgram::input_key_callback(const InputManager::KeyCallbackArgs& key) {
}
void MainProgram::input_text_key_callback(const InputManager::KeyCallbackArgs& key) {
switch(key.key) {
case InputManager::KEY_GENERIC_ESCAPE: {
if(key.down && !key.repeat)
toolbar.close_chatbox();
break;
}
}
g.input_text_key_callback(key);
if(world)
world->input_text_key_callback(key);

View file

@ -16,6 +16,7 @@
#include "MainProgram.hpp"
#include "InputManager.hpp"
#include "ResourceDisplay/ImageResourceDisplay.hpp"
#include "RichText/TextStyleModifier.hpp"
#include "VersionConstants.hpp"
#include "World.hpp"
#include <SDL3/SDL_dialog.h>
@ -52,6 +53,7 @@
#include "GUIStuff/Elements/DropDown.hpp"
#include "GUIStuff/Elements/SVGIcon.hpp"
#include "GUIStuff/Elements/MovableTabList.hpp"
#include "GUIStuff/Elements/TextParagraph.hpp"
#define UPDATE_DOWNLOAD_URL "https://infinipaint.com/download.html"
#define UPDATE_NOTIFICATION_URL "https://infinipaint.com/updateNotificationVersion.txt"
@ -257,6 +259,31 @@ void Toolbar::update() {
}
return false;
});
if(!chatboxOpen) {
for(auto& chatMessage : main.world->chatMessages) {
bool wasShown = chatMessage.time < ChatMessage::DISPLAY_TIME;
chatMessage.time.update_time_since();
bool isShown = chatMessage.time < ChatMessage::DISPLAY_TIME;
bool isFading = chatMessage.time >= ChatMessage::FADE_START_TIME;
if((isFading && isShown) || (wasShown && !isShown))
main.g.gui.set_to_layout();
}
}
}
void Toolbar::open_chatbox() {
if(!chatboxOpen) {
chatMessageInput.clear();
chatboxOpen = true;
main.g.gui.set_to_layout();
}
}
void Toolbar::close_chatbox() {
if(chatboxOpen) {
chatboxOpen = false;
main.g.gui.set_to_layout();
}
}
void Toolbar::layout_run() {
@ -1021,149 +1048,139 @@ void Toolbar::layer_menu(Element* layerMenuButton) {
});
}
std::unique_ptr<skia::textlayout::Paragraph> Toolbar::build_paragraph_from_chat_message(const ChatMessage& message, float alpha) {
RichText::TextData Toolbar::build_paragraph_from_chat_message(const ChatMessage& message, float alpha) {
auto& gui = main.g.gui;
auto& io = gui.io;
skia::textlayout::ParagraphStyle pStyle;
pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft);
skia::textlayout::TextStyle tStyle;
tStyle.setFontSize(io.fontSize);
tStyle.setFontFamilies(main.fonts->get_default_font_families());
tStyle.setFontStyle(SkFontStyle::Bold());
tStyle.setForegroundColor(SkPaint{color_mul_alpha(message.type == ChatMessage::JOIN ? io.theme->warningColor : io.theme->frontColor1, alpha)});
pStyle.setTextStyle(tStyle);
RichText::TextData toRet;
skia::textlayout::ParagraphBuilderImpl a(pStyle, io.fonts->collection, SkUnicodes::ICU::Make());
if(message.type == ChatMessage::JOIN) {
std::string messageName = message.name + " ";
a.addText(messageName.c_str(), messageName.length());
}
else {
std::string messageName = "[" + message.name + "] ";
a.addText(messageName.c_str(), messageName.length());
}
auto& par = toRet.paragraphs.emplace_back();
tStyle.setFontStyle(SkFontStyle::Normal());
a.pushStyle(tStyle);
a.addText(message.message.c_str(), message.message.length());
RichText::PositionedTextStyleMod& positionedModInit = toRet.tStyleMods.emplace_back();
positionedModInit.pos = {0, 0};
positionedModInit.mods[RichText::TextStyleModifier::ModifierType::WEIGHT] = std::make_shared<RichText::WeightTextStyleModifier>(SkFontStyle::Weight::kBold_Weight);
positionedModInit.mods[RichText::TextStyleModifier::ModifierType::COLOR] = std::make_shared<RichText::ColorTextStyleModifier>(convert_vec4<Vector4f>(color_mul_alpha(message.type == ChatMessage::JOIN ? io.theme->warningColor : io.theme->frontColor1, alpha)));
if(message.type == ChatMessage::JOIN)
par.text += message.name + " ";
else
par.text += "[" + message.name + "] ";
return a.Build();
RichText::PositionedTextStyleMod& positionedModMessage = toRet.tStyleMods.emplace_back();
positionedModMessage.pos = {0, par.text.size()};
positionedModMessage.mods[RichText::TextStyleModifier::ModifierType::WEIGHT] = std::make_shared<RichText::WeightTextStyleModifier>(SkFontStyle::Weight::kNormal_Weight);
par.text += message.message;
return toRet;
}
void Toolbar::chat_box() {
//constexpr float CHATBOX_WIDTH = 700;
//if(main.world->netObjMan.is_connected()) {
// Clay_ElementId localId = CLAY_ID_LOCAL("INFINIPAINT CHAT BOX OPEN BUTTON");
// CLAY(localId, {
// .layout = {
// .layoutDirection = CLAY_LEFT_TO_RIGHT
// },
// .floating = {.offset = {static_cast<float>(io.theme->padding1), -static_cast<float>(io.theme->padding1)}, .attachPoints = {.element = CLAY_ATTACH_POINT_LEFT_BOTTOM, .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
// }) {
// gui.obstructing_window(localId);
// gui.push_id("chat box open button");
// if(gui.svg_icon_button("Chat Open Button", "data/icons/chat.svg", chatBoxState != CHATBOXSTATE_CLOSE)) {
// if(chatBoxState == CHATBOXSTATE_CLOSE)
// chatBoxState = CHATBOXSTATE_JUSTOPEN;
// else
// chatBoxState = CHATBOXSTATE_CLOSE;
// }
// gui.pop_id();
// }
//}
//Clay_ElementId localId = CLAY_ID_LOCAL("INFINIPAINT CHAT BOX OPEN");
//CLAY(localId, {
// .layout = {
// .sizing = {.width = CLAY_SIZING_FIXED(CHATBOX_WIDTH), .height = CLAY_SIZING_FIT(0) },
// .childGap = 0,
// .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_BOTTOM},
// .layoutDirection = CLAY_TOP_TO_BOTTOM
// },
// .floating = {.offset = {60 + static_cast<float>(io.theme->padding1), -static_cast<float>(io.theme->padding1)}, .attachPoints = {.element = CLAY_ATTACH_POINT_LEFT_BOTTOM, .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
//}) {
// gui.obstructing_window(localId);
// gui.push_id("chat box");
// if(chatBoxState == CHATBOXSTATE_JUSTOPEN || chatBoxState == CHATBOXSTATE_OPEN) {
// gui.push_id("messages");
// CLAY_AUTO_ID({
// .layout = {
// .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0) },
// .padding = CLAY_PADDING_ALL(0),
// .childGap = 0,
// .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_BOTTOM},
// .layoutDirection = CLAY_TOP_TO_BOTTOM
// },
// .backgroundColor = convert_vec4<Clay_Color>(io.theme->backColor1)
// }) {
// int id = 0;
// for(auto& chatMessage : main.world->chatMessages | std::views::reverse) {
// gui.push_id(id);
// gui.text_paragraph("text", build_paragraph_from_chat_message(chatMessage, 1.0f), CHATBOX_WIDTH);
// gui.pop_id();
// id++;
// }
// }
// gui.pop_id();
auto& gui = main.g.gui;
auto& io = gui.io;
// gui.left_to_right_line_layout([&]() {
// gui.input_text("message input", &chatMessageInput);
// if(gui.text_button("send button", "Send", false, true)) {
// if(!chatMessageInput.empty())
// main.world->send_chat_message(chatMessageInput);
// }
// });
// gui.push_id("message input");
// GUIStuff::GUIManager::ElementContainer& a = gui.elements[gui.idStack];
// GUIStuff::TextBox<std::string>& t = *(GUIStuff::TextBox<std::string>*)a.elem.get();
// gui.pop_id();
// if(chatBoxState == CHATBOXSTATE_JUSTOPEN) {
// t.selection.selected = true;
// chatBoxState = CHATBOXSTATE_OPEN;
// }
// if(io.key.escape)
// t.selection.selected = false;
// if(io.key.enter) {
// main.world->send_chat_message(chatMessageInput);
// t.selection.selected = false;
// }
// if(!t.selection.selected) {
// chatMessageInput.clear();
// chatBoxState = CHATBOXSTATE_CLOSE;
// }
// }
// else {
// constexpr float DISPLAY_TIME = 8.0f;
// constexpr float FADE_START_TIME = 7.0f;
// gui.push_id("messages popup");
// int id = 0;
// for(auto& chatMessage : main.world->chatMessages | std::views::reverse) {
// chatMessage.time.update_time_since();
// if(chatMessage.time < DISPLAY_TIME) {
// float a = 1.0f - lerp_time<float>(chatMessage.time, DISPLAY_TIME, FADE_START_TIME);
// Clay_ElementId elemId = CLAY_IDI_LOCAL("CHAT MESSAGE ID", id);
// CLAY(elemId, {
// .layout = {
// .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0) },
// .padding = CLAY_PADDING_ALL(0),
// .childGap = 0,
// .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
// .layoutDirection = CLAY_TOP_TO_BOTTOM
// },
// .backgroundColor = convert_vec4<Clay_Color>(color_mul_alpha(io.theme->backColor1, a)),
// }) {
// gui.push_id(id);
// gui.obstructing_window(elemId);
// gui.text_paragraph("text", build_paragraph_from_chat_message(chatMessage, a), CHATBOX_WIDTH);
// gui.pop_id();
// }
// }
// id++;
// }
// gui.pop_id();
// }
// gui.pop_id();
//}
constexpr float CHATBOX_WIDTH = 700;
if(main.world->netObjMan.is_connected()) {
gui.element<LayoutElement>("Infinipaint chat box open button", [&](LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
.floating = {.offset = {static_cast<float>(io.theme->padding1), -static_cast<float>(io.theme->padding1)}, .zIndex = gui.get_z_index(), .attachPoints = {.element = CLAY_ATTACH_POINT_LEFT_BOTTOM, .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
}) {
svg_icon_button(gui, "Chat open button", "data/icons/chat.svg", {
.onClick = [&] {
chatboxOpen = !chatboxOpen;
chatMessageInput.clear();
}
});
}
});
}
gui.element<LayoutElement>("Infinipaint chat box", [&](LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(CHATBOX_WIDTH), .height = CLAY_SIZING_FIT(0) },
.childGap = 0,
.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_BOTTOM},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.floating = {.offset = {60 + static_cast<float>(io.theme->padding1), -static_cast<float>(io.theme->padding1)}, .zIndex = gui.get_z_index(), .attachPoints = {.element = CLAY_ATTACH_POINT_LEFT_BOTTOM, .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT}
}) {
if(chatboxOpen) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0) },
.padding = CLAY_PADDING_ALL(0),
.childGap = 0,
.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_BOTTOM},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(io.theme->backColor1)
}) {
int id = 0;
for(auto& chatMessage : main.world->chatMessages | std::views::reverse) {
gui.new_id(id++, [&] {
gui.element<TextParagraph>("text", TextParagraph::Data{
.text = build_paragraph_from_chat_message(chatMessage, 1.0f),
.width = CHATBOX_WIDTH
});
});
}
}
left_to_right_line_layout(gui, [&] {
input_text(gui, "message input", &chatMessageInput, {
.onEnter = [&] {
if(!chatMessageInput.empty())
main.world->send_chat_message(chatMessageInput);
chatboxOpen = false;
},
.onDeselect = [&] {
chatboxOpen = false;
gui.set_to_layout();
}
})->select();
text_button(gui, "send button", "Send", {
.instantResponse = true,
.onClick = [&] {
if(!chatMessageInput.empty())
main.world->send_chat_message(chatMessageInput);
chatboxOpen = false;
}
});
});
}
else {
gui.new_id("Message popups", [&] {
int id = 0;
for(auto& chatMessage : main.world->chatMessages | std::views::reverse) {
chatMessage.time.update_time_since();
if(chatMessage.time < ChatMessage::DISPLAY_TIME) {
gui.new_id(id++, [&] {
float a = 1.0f - lerp_time<float>(chatMessage.time, ChatMessage::DISPLAY_TIME, ChatMessage::FADE_START_TIME);
gui.element<LayoutElement>("Chat message", [&](LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0) },
.padding = CLAY_PADDING_ALL(0),
.childGap = 0,
.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(color_mul_alpha(io.theme->backColor1, a)),
}) {
gui.element<TextParagraph>("message", TextParagraph::Data{
.text = build_paragraph_from_chat_message(chatMessage, a),
.width = CHATBOX_WIDTH
});
}
});
});
}
}
});
}
}
});
}
void Toolbar::global_log() {
@ -2136,7 +2153,7 @@ void Toolbar::file_picker_gui() {
}
});
input_path_field(gui, "file picker path", "Path", &filePicker.currentSearchPath, {
.updateEveryEdit = false,
.fileTypeRestriction = std::filesystem::file_type::directory,
.onEdit = [&] {
file_picker_gui_refresh_entries();
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "GUIStuff/Elements/ScrollArea.hpp"
#include "GUIStuff/Elements/TextParagraph.hpp"
#include "GUIStuff/GUIManager.hpp"
#include "DrawData.hpp"
#include "Helpers/FileDownloader.hpp"
@ -26,6 +27,8 @@ class Toolbar {
};
struct ChatMessage {
static constexpr float DISPLAY_TIME = 8.0f;
static constexpr float FADE_START_TIME = 7.0f;
std::string name;
std::string message;
enum Type {
@ -73,6 +76,9 @@ class Toolbar {
void load_palettes();
void load_licenses();
void open_chatbox();
void close_chatbox();
GUIStuff::Element* colorLeftButton;
Vector4f* colorLeft = nullptr;
ColorSelectorData colorLeftData;
@ -171,11 +177,7 @@ class Toolbar {
float finalCalculatedGuiScale = 1.0f;
enum {
CHATBOXSTATE_OPEN,
CHATBOXSTATE_JUSTOPEN,
CHATBOXSTATE_CLOSE
} chatBoxState = CHATBOXSTATE_CLOSE;
bool chatboxOpen = false;
enum OptionsMenuType {
HOST_MENU,
@ -226,7 +228,7 @@ class Toolbar {
};
static NativeFilePicker nativeFilePicker;
std::unique_ptr<skia::textlayout::Paragraph> build_paragraph_from_chat_message(const ChatMessage& message, float alpha);
RichText::TextData build_paragraph_from_chat_message(const ChatMessage& message, float alpha);
void load_icons_at(const std::filesystem::path& pathToLoad);