Added top toolbar options

This commit is contained in:
Yousef Khadadeh 2026-05-12 15:19:43 +03:00
parent fc8f414b67
commit fd2ce71db6
12 changed files with 205 additions and 46 deletions

View file

@ -782,15 +782,11 @@ void DrawingProgram::get_used_resources(std::unordered_set<NetworkingObjects::Ne
layerMan.get_used_resources(resourceSet);
}
Vector4f* DrawingProgram::color_picker_left(Vector4f* oldColor) {
Vector4f* DrawingProgram::color_picker_color(Vector4f* oldColor) {
if(oldColor == &world.main.toolConfig.globalConf.foregroundColor)
return oldColor;
else if(oldColor == &world.main.toolConfig.globalConf.backgroundColor)
return oldColor;
return nullptr;
}
Vector4f* DrawingProgram::color_picker_right(Vector4f* oldColor) {
return drawTool->color_picker_color(oldColor);
}

View file

@ -58,8 +58,7 @@ class DrawingProgram {
CanvasComponentContainer::ObjInfo* add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos);
void get_used_resources(std::unordered_set<NetworkingObjects::NetObjID>& resourceSet);
Vector4f* color_picker_left(Vector4f* oldColor);
Vector4f* color_picker_right(Vector4f* oldColor);
Vector4f* color_picker_color(Vector4f* oldColor);
bool phone_gui_tool_specific_bottom_toolbar_exists();
void phone_gui_tool_specific_bottom_toolbar(PhoneDrawingProgramScreen& t);

View file

@ -291,9 +291,14 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin
offsetY = gui.io.safeWindowRect.min.y();
else if(offsetY + dropdownHeight > gui.io.safeWindowRect.max.y())
offsetY -= (offsetY + dropdownHeight) - gui.io.safeWindowRect.max.y();
float offsetX = 0.0f;
if(isRightSide)
offsetX = bb.max.x() + d.dropdownOffset;
else
offsetX = bb.min.x() - d.entrySize.x() - d.dropdownOffset;
floatConfig = {
.offset = {
.x = isRightSide ? bb.max.x() + d.dropdownOffset : bb.min.x() - bb.width() - d.dropdownOffset,
.x = offsetX,
.y = offsetY
},
.zIndex = gui.get_z_index(),
@ -306,9 +311,10 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin
maxHeight = gui.io.safeWindowRect.height();
}
void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, const DropDownPopupLayout& d) {
void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDownPopupLayout d) {
if(!d.button->get_bb().has_value())
return;
d.entrySize.x() = std::min(d.entrySize.x(), gui.io.windowSize.x());
gui.element<LayoutElement>(id, [&](LayoutElement* l, const Clay_ElementId& lId) {
float dropdownHeight = d.entrySize.y() * d.entryCount + d.dropdownOffset;
Clay_FloatingElementConfig floatConfig;

View file

@ -50,6 +50,6 @@ struct DropDownPopupLayout {
std::function<void(size_t i)> entryLayout;
};
void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, const DropDownPopupLayout& d);
void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDownPopupLayout d);
}}

View file

@ -29,6 +29,7 @@ template <typename T, typename OptT> TextBoxData<T> textbox_options_to_data(cons
toRet.onEnter = options.onEnter;
toRet.onEdit = options.onEdit;
toRet.immutable = options.immutable;
toRet.decorations = options.decorations;
toRet.onSelect = options.onSelect;
toRet.onDeselect = options.onDeselect;
toRet.emptyText = options.emptyText;
@ -37,6 +38,7 @@ template <typename T, typename OptT> TextBoxData<T> textbox_options_to_data(cons
struct TextBoxOptions {
bool immutable = false;
bool decorations = true;
std::string emptyText;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -47,6 +49,7 @@ struct TextBoxOptions {
struct TextBoxScalarOptions {
int decimalPrecision = 0;
bool immutable = false;
bool decorations = true;
std::string emptyText;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -57,6 +60,7 @@ struct TextBoxScalarOptions {
struct TextBoxScalarsOptions {
int decimalPrecision = 0;
bool immutable = false;
bool decorations = true;
std::string emptyText;
std::function<void(size_t)> onEnter;
std::function<void(size_t)> onEdit;
@ -67,6 +71,7 @@ struct TextBoxScalarsOptions {
struct TextBoxPathOptions {
std::filesystem::file_type fileTypeRestriction;
bool immutable = false;
bool decorations = true;
std::string emptyText;
std::function<void()> onEnter;
std::function<void()> onEdit;
@ -77,6 +82,7 @@ struct TextBoxPathOptions {
struct TextBoxHexColorOptions {
bool hasAlpha = true;
bool immutable = false;
bool decorations = true;
std::string emptyText;
std::function<void()> onEnter;
std::function<void()> onEdit;

View file

@ -33,6 +33,7 @@ template <typename T> struct TextBoxData {
std::function<std::string(const T&)> toStr;
bool singleLine = true;
bool immutable = false;
bool decorations = true;
InputManager::TextInputProperties textInputProps;
std::string emptyText;
std::function<void()> onEdit;

View file

@ -66,14 +66,16 @@ template <typename T> void TextBox<T>::clay_draw(SkCanvas* canvas, UpdateInputDa
canvas->save();
SkRect r = SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height());
canvas->drawRect(r, SkPaint(io.theme->backColor2));
SkPaint outline(is_selected() ? io.theme->fillColor1 : io.theme->backColor2);
outline.setStroke(true);
outline.setStrokeWidth(2.0f);
outline.setAntiAlias(skiaAA);
canvas->drawRoundRect(r, 2.0f, 2.0f, outline);
if(userInfo.decorations) {
canvas->drawRect(r, SkPaint(io.theme->backColor2));
SkPaint outline(is_selected() ? io.theme->fillColor1 : io.theme->backColor2);
outline.setStroke(true);
outline.setStrokeWidth(2.0f);
outline.setAntiAlias(skiaAA);
canvas->drawRoundRect(r, 2.0f, 2.0f, outline);
}
canvas->clipRect(SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height()));
canvas->clipRect(r);
float yOffset = bb.height() * 0.5f;
yOffset -= textbox->get_height() * 0.5f;
@ -105,7 +107,6 @@ template <typename T> void TextBox<T>::select() {
if(isEmptyText)
textbox->clear_text();
edit = std::make_unique<RichTextUserInput>(CustomEvents::text_box_get_new_id(), textbox, cur, nullptr);
Logger::get().log("INFO", "Select");
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
}
}
@ -114,7 +115,6 @@ template <typename T> void TextBox<T>::deselect() {
if(is_selected()) {
edit = nullptr;
if(userInfo.onDeselect) userInfo.onDeselect();
Logger::get().log("INFO", "Deselect");
CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{});
reset_textbox_text();
populate_empty_textbox();

View file

@ -92,13 +92,140 @@ void PhoneDrawingProgramScreen::top_toolbar() {
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<FileSelectScreen>(main); });
}
});
text_label(gui, main.world->name);
top_toolbar_remaining_area();
}
});
}
});
}
void PhoneDrawingProgramScreen::top_toolbar_remaining_area() {
auto& gui = main.g.gui;
std::vector<TopToolbarRemainingAreaButton> listOfTopToolbarButtons = {
TopToolbarRemainingAreaButton{
.name = "Undo",
.svgPath = "data/icons/undo.svg",
.onClick = [&] {
main.world->undo_with_checks();
}
},
TopToolbarRemainingAreaButton{
.name = "Redo",
.svgPath = "data/icons/redo.svg",
.onClick = [&] {
main.world->redo_with_checks();
}
},
};
gui.element<LayoutElement>("remaining area", [&](LayoutElement* l, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
}
}) {
if(l->get_bb().has_value()) {
constexpr float MINIMUM_SPACE_FOR_NAME = 100.0f;
constexpr float BUTTON_WIDTH = BIG_BUTTON_SIZE;
float spaceForWorldName = l->get_bb().value().width() - BUTTON_WIDTH;
if(spaceForWorldName < 4.0f)
spaceForWorldName = 4.0f;
size_t buttonFreeSpace;
for(buttonFreeSpace = 0; buttonFreeSpace < listOfTopToolbarButtons.size(); buttonFreeSpace++) {
if(spaceForWorldName <= MINIMUM_SPACE_FOR_NAME + BUTTON_WIDTH)
break;
spaceForWorldName -= BUTTON_WIDTH;
}
input_text(gui, "world name text", &main.world->name, {
.decorations = false,
.onSelect = [&] {
fileOldPath = main.world->filePath;
},
.onDeselect = [&] {
std::filesystem::remove(fileOldPath);
std::filesystem::remove(fileOldPath.parent_path() / (fileOldPath.stem().string() + ".jpg"));
main.world->autosave_to_directory(main.world->filePath.parent_path());
}
});
gui.new_id("visible buttons", [&] {
for(size_t i = 0; i < buttonFreeSpace; i++) {
gui.new_id(i, [&] {
svg_icon_button(gui, "button", listOfTopToolbarButtons.front().svgPath, {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.onClick = listOfTopToolbarButtons.front().onClick
});
listOfTopToolbarButtons.erase(listOfTopToolbarButtons.begin());
});
}
});
Element* e = svg_icon_button(gui, "Hidden buttons", "data/icons/RemixIcon/more-2-fill.svg", {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.onClick = [&] {
topToolbarButtonPopupOpen = !topToolbarButtonPopupOpen;
}
});
if(topToolbarButtonPopupOpen)
top_toolbar_hidden_button_popup(e, listOfTopToolbarButtons);
}
}
});
}
void PhoneDrawingProgramScreen::top_toolbar_hidden_button_popup(GUIStuff::Element* b, std::vector<TopToolbarRemainingAreaButton> l) {
auto& gui = main.g.gui;
l.emplace_back(TopToolbarRemainingAreaButton{
.name = "Canvas Color",
.svgPath = "data/icons/RemixIcon/settings-3-line.svg",
.onClick = [&] {
settingsMenuPopup = SettingsMenuPopup::SETTINGS;
colorPickerPtr = &backgroundColorTemporary;
colorPickerData = {
.onChange = [&] {
main.world->canvasTheme.set_back_color({backgroundColorTemporary.x(), backgroundColorTemporary.y(), backgroundColorTemporary.z()});
}
};
}
});
gui.set_z_index(gui.get_z_index() + 1, [&] {
dropdown_many_element_popup_layout(gui, "top toolbar hidden button popup", {
.button = b,
.isOpen = &topToolbarButtonPopupOpen,
.entrySize = {150.0f, 30.0f},
.entryCount = l.size(),
.entryLayout = [&] (size_t i) {
auto& listItem = l[i];
gui.element<SelectableButton>("button", SelectableButton::Data{
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.onClick = [&, oC = listItem.onClick] {
oC();
topToolbarButtonPopupOpen = false;
},
.innerContent = [&] (auto&) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER},
}
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)},
}
}) {
gui.element<SVGIcon>("icon", listItem.svgPath);
}
mutable_text_label(gui, "text", listItem.name);
}
}
});
}
});
});
}
void PhoneDrawingProgramScreen::bottom_toolbar() {
auto& gui = main.g.gui;
auto& io = gui.io;
@ -144,7 +271,8 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() {
colorPickerPtr = nullptr;
break;
case SettingsMenuPopup::SETTINGS:
colorPickerPtr = main.world->drawProg.color_picker_right(colorPickerPtr);
if(colorPickerPtr != &backgroundColorTemporary)
colorPickerPtr = main.world->drawProg.color_picker_color(colorPickerPtr);
if(colorPickerPtr) {
if(colorPickerPopupData.screenType == ColorPickerPopupData::ScreenType::NORMAL) {
CLAY_AUTO_ID({
@ -167,8 +295,7 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() {
tool_settings_popup();
}
break;
case SettingsMenuPopup::FG_COLOR:
case SettingsMenuPopup::BG_COLOR:
case SettingsMenuPopup::COLOR_CHANGE:
if(colorPickerPopupData.screenType == ColorPickerPopupData::ScreenType::NORMAL) {
CLAY_AUTO_ID({
.layout = {
@ -176,9 +303,7 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() {
}
}) {}
}
colorPickerData = ColorSelectorData();
colorPickerPtr = nullptr;
color_settings_popup(settingsMenuPopup == SettingsMenuPopup::FG_COLOR ? &main.toolConfig.globalConf.foregroundColor : &main.toolConfig.globalConf.backgroundColor, {}, nullptr);
color_settings_popup(colorPickerPtr, colorPickerData, nullptr);
break;
}
}
@ -612,19 +737,35 @@ void PhoneDrawingProgramScreen::bottom_extra_toolbar_gui() {
color_button(gui, "foreground color", &main.toolConfig.globalConf.foregroundColor, {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.isSelected = settingsMenuPopup == SettingsMenuPopup::FG_COLOR,
.isSelected = settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.foregroundColor,
.hasAlpha = true,
.onClick = [&] {
settingsMenuPopup = (settingsMenuPopup == SettingsMenuPopup::FG_COLOR) ? SettingsMenuPopup::NONE : SettingsMenuPopup::FG_COLOR;
if(settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.foregroundColor) {
colorPickerPtr = nullptr;
settingsMenuPopup = SettingsMenuPopup::NONE;
}
else {
colorPickerPtr = &main.toolConfig.globalConf.foregroundColor;
settingsMenuPopup = SettingsMenuPopup::COLOR_CHANGE;
}
colorPickerData = ColorSelectorData();
}
});
color_button(gui, "background color", &main.toolConfig.globalConf.backgroundColor, {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.isSelected = settingsMenuPopup == SettingsMenuPopup::BG_COLOR,
.isSelected = settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.backgroundColor,
.hasAlpha = true,
.onClick = [&] {
settingsMenuPopup = (settingsMenuPopup == SettingsMenuPopup::BG_COLOR) ? SettingsMenuPopup::NONE : SettingsMenuPopup::BG_COLOR;
if(settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.backgroundColor) {
colorPickerPtr = nullptr;
settingsMenuPopup = SettingsMenuPopup::NONE;
}
else {
colorPickerPtr = &main.toolConfig.globalConf.backgroundColor;
settingsMenuPopup = SettingsMenuPopup::COLOR_CHANGE;
}
colorPickerData = ColorSelectorData();
}
});
}
@ -659,7 +800,7 @@ void PhoneDrawingProgramScreen::color_selector_button(const char* id, Vector4f*
color_selector(color, {
.onChange = colorSelectorData.onChange,
.onSelect = colorSelectorData.onSelect,
.onDeselect = colorSelectorData.onDeselect,
.onDeselect = colorSelectorData.onDeselect
});
}
});

View file

@ -54,21 +54,31 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
void color_settings_popup(Vector4f* color, const ColorSelectorData& d, std::function<void()> backOnPaletteButton = nullptr, bool setAlphaToOne = false, bool setToTransparentCancelButton = false);
void reset_color_picker_popup_data();
private:
bool topToolbarButtonPopupOpen = false;
std::filesystem::path fileOldPath;
struct TopToolbarRemainingAreaButton {
std::string name;
std::string svgPath;
std::function<void()> onClick;
};
enum class SettingsMenuPopup {
NONE,
SETTINGS,
COLOR_CHANGE
} settingsMenuPopup = SettingsMenuPopup::NONE;
Vector4f backgroundColorTemporary;
Vector4f* colorPickerPtr = nullptr;
ColorSelectorData colorPickerData;
void default_bottom_toolbar();
void bottom_toolbar_gui();
void bottom_extra_toolbar_gui();
void save_files();
void top_toolbar();
void top_toolbar_remaining_area();
void top_toolbar_hidden_button_popup(GUIStuff::Element* b, std::vector<TopToolbarRemainingAreaButton> l);
void bottom_toolbar();
void main_display();
void tool_settings_popup();
enum class SettingsMenuPopup {
NONE,
SETTINGS,
FG_COLOR,
BG_COLOR
} settingsMenuPopup = SettingsMenuPopup::NONE;
Vector4f* colorPickerPtr = nullptr;
ColorSelectorData colorPickerData;
};

View file

@ -1186,7 +1186,7 @@ void Toolbar::drawing_program_gui() {
}) {
main.world->drawProg.toolbar_gui(*this);
colorLeft = main.world->drawProg.color_picker_left(colorLeft);
colorLeft = main.world->drawProg.color_picker_color(colorLeft);
if(colorLeft)
color_picker_window("Drawing program gui color picker left", &colorLeft, colorLeftButton, colorLeftData);
CLAY_AUTO_ID({
@ -1194,7 +1194,7 @@ void Toolbar::drawing_program_gui() {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
}
}) {}
colorRight = main.world->drawProg.color_picker_right(colorRight);
colorRight = main.world->drawProg.color_picker_color(colorRight);
if(colorRight)
color_picker_window("Drawing program gui color picker right", &colorRight, colorRightButton, colorRightData);

View file

@ -470,10 +470,10 @@ void World::autosave_to_directory(const std::filesystem::path& directoryToSaveAt
catch(...) {
}
std::string nameToSaveUnder = ensure_string_unique(strList, name);
save_to_file(directoryToSaveAt / std::filesystem::path(nameToSaveUnder + "." + FILE_EXTENSION), true);
save_to_file(directoryToSaveAt / std::filesystem::path(nameToSaveUnder + "." + FILE_EXTENSION));
}
void World::save_to_file(const std::filesystem::path& filePathToSaveAt, bool disableThumbnailSaving) {
void World::save_to_file(const std::filesystem::path& filePathToSaveAt) {
try {
filePath = filePathToSaveAt;
@ -505,7 +505,7 @@ void World::save_to_file(const std::filesystem::path& filePathToSaveAt, bool dis
#else
if(!SDL_SaveFile(filePath.string().c_str(), f.view().data(), f.view().size()))
throw std::runtime_error("SDL_SaveFile failed with error: " + std::string(SDL_GetError()));
else if(saveThumbnail && !disableThumbnailSaving) {
else if(saveThumbnail) {
Vector2f imageCenter{main.window.size.x() * 0.5f, main.window.size.y() * 0.5f};
float imageDim = std::max(main.window.size.x(), main.window.size.y());
Vector2f imageDimVec{imageDim * 0.5f, imageDim * 0.5f};

View file

@ -84,7 +84,7 @@ class World {
bool clientStillConnecting = false;
void autosave_to_directory(const std::filesystem::path& directoryToSaveAt);
void save_to_file(const std::filesystem::path& filePathToSaveAt, bool disableThumbnailSaving = false);
void save_to_file(const std::filesystem::path& filePathToSaveAt);
void load_from_file(const std::filesystem::path& filePathToLoadFrom, std::string_view buffer);
void undo_with_checks();