Added more settings and actions to phone version

This commit is contained in:
Yousef Khadadeh 2026-05-12 21:13:31 +03:00
parent fd2ce71db6
commit 1b99c64c1b
21 changed files with 388 additions and 179 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M21 15V18H24V20H21V23H19V20H16V18H19V15H21ZM21.0082 3C21.556 3 22 3.44495 22 3.9934V13H20V5H4V18.999L14 9L17 12V14.829L14 11.8284L6.827 19H14V21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082ZM8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7Z"></path></svg>

After

Width:  |  Height:  |  Size: 438 B

View file

@ -719,6 +719,8 @@ void DrawingProgram::add_file_to_canvas_by_path(const std::filesystem::path& fil
CanvasComponentContainer* newContainer = new CanvasComponentContainer(world.netObjMan, CanvasComponentType::IMAGE);
ImageCanvasComponent& img = static_cast<ImageCanvasComponent&>(newContainer->get_comp());
newContainer->coords = world.drawData.cam.c;
if(world.main.g.gui.io.isTouchDevice)
newContainer->coords.scale_about_double(world.drawData.cam.c.from_space(dropPos), 1.0 / world.main.get_scale_and_density_factor_gui());
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
Vector2f imDim = Vector2f{world.main.window.size.x() * imWidth, world.main.window.size.x() * imHeight} * display->get_dimension_scale();

View file

@ -404,7 +404,7 @@ void TextBoxEditTool::phone_bottom_toolbar_gui(PhoneDrawingProgramScreen& t) {
gui.set_z_index(gui.get_z_index() + 1, [&] {
dropdown_many_element_popup_layout(gui, "Font Picker Popup", DropDownPopupLayout{
.button = fontPickerButton,
.isOpen = &fontPickerIsOpen,
.clickAwayCallback = [&] { fontPickerIsOpen = false; },
.entrySize = {200.0f, BIG_BUTTON_SIZE},
.entryCount = FontPicker::sorted_font_list(gui).size(),
.entryLayout = [&](size_t i) {

View file

@ -93,7 +93,7 @@ void ScreenshotTool::gui_toolbox(Toolbar& t) {
take_screenshot("a" + controls.typeSelections[screenshotConfig.selectedType], screenshotConfig.selectedType);
#else
// We can't actually use the extension from the callback, so we have to set the extension of choice beforehand
Toolbar::ExtensionFilter setExtensionFilter;
Screen::ExtensionFilter setExtensionFilter;
switch(screenshotConfig.selectedType) {
case WorldScreenshotInfo::ScreenshotType::JPG:
setExtensionFilter = {"JPEG", "jpg;jpeg"};

View file

@ -65,7 +65,7 @@ void TextBoxTool::input_mouse_button_on_canvas_callback(const InputManager::Mous
newTextBox.d.editing = true;
newContainer->coords = drawP.world.drawData.cam.c;
if(drawP.world.main.g.gui.io.isTouchDevice)
newContainer->coords.scale_about_double(drawP.world.drawData.cam.c.from_space(startAt), 1.0 / drawP.world.main.g.final_gui_scale());
newContainer->coords.scale_about_double(drawP.world.drawData.cam.c.from_space(startAt), 1.0 / drawP.world.main.get_scale_and_density_factor_gui());
objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newContainer);
}
else if(!button.down && objInfoBeingEdited) {

View file

@ -19,6 +19,7 @@
#include "ButtonHelpers.hpp"
#include "../Elements/SVGIcon.hpp"
#include "../Elements/LayoutElement.hpp"
#include "Helpers/ConvertVec.hpp"
#include "TextLabelHelpers.hpp"
namespace GUIStuff { namespace ElementHelpers {
@ -37,7 +38,7 @@ void text_button(GUIManager& gui, const char* id, std::string_view text, const T
};
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = options.wide ? CLAY_SIZING_GROW(0) : CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0) },
.sizing = {.width = options.wide ? CLAY_SIZING_GROW(0) : CLAY_SIZING_FIT(0), .height = options.growHeight ? CLAY_SIZING_GROW(0) : CLAY_SIZING_FIT(0) },
.childAlignment = {.x = options.centered ? CLAY_ALIGN_X_CENTER : CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
}
}) {

View file

@ -41,6 +41,7 @@ struct TextButtonOptions {
bool isSelected = false;
bool instantResponse = false;
bool wide = false;
bool growHeight = false;
bool centered = true;
std::function<void()> onClick;

View file

@ -18,12 +18,14 @@
#pragma once
#include "../GUIManager.hpp"
#include "Helpers/ConvertVec.hpp"
#include "TextBoxHelpers.hpp"
#include "../Elements/ColorPicker.hpp"
namespace GUIStuff { namespace ElementHelpers {
struct ColorPickerItemsOptions {
bool forceAspectRatioOnColorPicker = true;
bool hasAlpha = true;
std::function<void()> onEdit;
std::function<void()> onSelect;
@ -40,26 +42,28 @@ template <typename T> void color_picker_items(GUIManager& gui, const char* id, T
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
},
.aspectRatio = {1.0f}
.aspectRatio = {options.forceAspectRatioOnColorPicker ? 1.0f : 0.0f}
}) {
gui.element<ColorPicker<T>>("c", val, options.hasAlpha, ColorPickerData{ .onChange = fullOnEdit, .onHold = options.onSelect, .onRelease = options.onDeselect });
}
left_to_right_line_layout(gui, [&]() {
text_label(gui, "R");
input_color_component_255(gui, "r", &(*val)[0], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "G");
input_color_component_255(gui, "g", &(*val)[1], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "B");
input_color_component_255(gui, "b", &(*val)[2], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
if(options.hasAlpha) {
text_label(gui, "A");
input_color_component_255(gui, "a", &(*val)[3], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
}
});
left_to_right_line_layout(gui, [&]() {
text_label(gui, "Hex");
input_color_hex(gui, "h", val, { .hasAlpha = options.hasAlpha, .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
});
if(!gui.io.isTouchDevice) {
left_to_right_line_layout(gui, [&]() {
text_label(gui, "R");
input_color_component_255(gui, "r", &(*val)[0], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "G");
input_color_component_255(gui, "g", &(*val)[1], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "B");
input_color_component_255(gui, "b", &(*val)[2], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
if(options.hasAlpha) {
text_label(gui, "A");
input_color_component_255(gui, "a", &(*val)[3], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
}
});
left_to_right_line_layout(gui, [&]() {
text_label(gui, "Hex");
input_color_hex(gui, "h", val, { .hasAlpha = options.hasAlpha, .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
});
}
});
}

View file

@ -245,7 +245,7 @@ void bottom_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingEle
floatConfig = {
.offset = {
.x = xOffset,
.y = bb.max.y() + d.dropdownOffset
.y = bb.max.y() + d.popupOffset
},
.zIndex = gui.get_z_index(),
.attachPoints = {
@ -255,7 +255,7 @@ void bottom_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingEle
.attachTo = CLAY_ATTACH_TO_ROOT
};
maxHeight = std::max(gui.io.safeWindowRect.max.y() - bb.max.y() - d.dropdownOffset, 0.0f);
maxHeight = std::max(gui.io.safeWindowRect.max.y() - bb.max.y() - d.popupOffset, 0.0f);
}
void top_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingElementConfig& floatConfig, float& maxHeight, const DropDownPopupLayout& d) {
@ -271,7 +271,7 @@ void top_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingElemen
floatConfig = {
.offset = {
.x = xOffset,
.y = bb.min.y() - d.dropdownOffset
.y = bb.min.y() - d.popupOffset
},
.zIndex = gui.get_z_index(),
.attachPoints = {
@ -281,7 +281,7 @@ void top_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingElemen
.attachTo = CLAY_ATTACH_TO_ROOT
};
maxHeight = std::max(bb.min.y() - gui.io.safeWindowRect.min.y() - d.dropdownOffset, 0.0f);
maxHeight = std::max(bb.min.y() - gui.io.safeWindowRect.min.y() - d.popupOffset, 0.0f);
}
void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_FloatingElementConfig& floatConfig, float& maxHeight, bool isRightSide, const DropDownPopupLayout& d) {
@ -293,9 +293,9 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin
offsetY -= (offsetY + dropdownHeight) - gui.io.safeWindowRect.max.y();
float offsetX = 0.0f;
if(isRightSide)
offsetX = bb.max.x() + d.dropdownOffset;
offsetX = bb.max.x() + d.popupOffset;
else
offsetX = bb.min.x() - d.entrySize.x() - d.dropdownOffset;
offsetX = bb.min.x() - d.entrySize.x() - d.popupOffset;
floatConfig = {
.offset = {
.x = offsetX,
@ -314,9 +314,9 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin
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());
d.entrySize.x() = std::min(d.entrySize.x(), gui.io.safeWindowRect.width());
gui.element<LayoutElement>(id, [&](LayoutElement* l, const Clay_ElementId& lId) {
float dropdownHeight = d.entrySize.y() * d.entryCount + d.dropdownOffset;
float dropdownHeight = d.entrySize.y() * d.entryCount + d.popupOffset;
Clay_FloatingElementConfig floatConfig;
float maxHeight = gui.io.safeWindowRect.height();
auto& buttonBB = d.button->get_bb().value();
@ -356,9 +356,88 @@ void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDow
}
}, LayoutElement::Callbacks {
.onClick = [&, d](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& m) {
if(!l->mouseHovering && !l->childMouseHovering && m.down && !d.button->mouseHovering && d.isOpen) {
*d.isOpen = false;
gui.set_to_layout();
if(!l->mouseHovering && !l->childMouseHovering && !d.button->mouseHovering) {
if(m.down && d.clickAwayCallback) {
d.clickAwayCallback();
gui.set_to_layout();
}
if(!m.down && d.clickUpAwayCallback) {
d.clickUpAwayCallback();
gui.set_to_layout();
}
}
}
});
}
void attach_to_button_popup_layout(GUIManager& gui, const char* id, AttachToButtonPopupLayout d) {
if(!d.button->get_bb().has_value())
return;
d.popupSize.x() = std::min(d.popupSize.x(), std::abs(gui.io.safeWindowRect.width() - 10.0f));
d.popupSize.y() = std::min(d.popupSize.y(), std::abs(gui.io.safeWindowRect.height() - 10.0f));
float maxHeight = d.popupSize.y();
gui.element<LayoutElement>(id, [&](LayoutElement* l, const Clay_ElementId& lId) {
Clay_FloatingElementConfig floatConfig;
auto& buttonBB = d.button->get_bb().value();
if(buttonBB.max.y() + d.popupSize.y() > gui.io.safeWindowRect.max.y()) {
float rightSideWidth = gui.io.safeWindowRect.max.x() - buttonBB.max.x();
float leftSideWidth = buttonBB.min.x() - gui.io.safeWindowRect.min.x();
if(rightSideWidth > d.popupSize.x() || leftSideWidth > d.popupSize.x())
left_right_offset_setup(gui, d.popupSize.y(), floatConfig, maxHeight, rightSideWidth >= leftSideWidth, {
.popupOffset = d.popupOffset,
.button = d.button,
.entrySize = d.popupSize
});
else {
if(gui.io.safeWindowRect.max.y() - buttonBB.max.y() >= buttonBB.min.y() - gui.io.safeWindowRect.min.y())
bottom_offset_setup(gui, d.popupSize.y(), floatConfig, maxHeight, {
.popupOffset = d.popupOffset,
.button = d.button,
.entrySize = d.popupSize
});
else
top_offset_setup(gui, d.popupSize.y(), floatConfig, maxHeight, {
.popupOffset = d.popupOffset,
.button = d.button,
.entrySize = d.popupSize
});
}
}
else
bottom_offset_setup(gui, d.popupSize.y(), floatConfig, maxHeight, {
.popupOffset = d.popupOffset,
.button = d.button,
.entrySize = d.popupSize
});
if(maxHeight < d.popupSize.y())
d.popupSize.y() = maxHeight;
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(d.popupSize.x()), .height = CLAY_SIZING_FIXED(d.popupSize.y())},
.childGap = 0
},
.backgroundColor = convert_vec4<Clay_Color>(gui.io.theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(4),
.floating = floatConfig,
.border = {
.color = convert_vec4<Clay_Color>(gui.io.theme->fillColor2),
.width = CLAY_BORDER_OUTSIDE(1)
}
}) {
d.innerLayout();
}
}, LayoutElement::Callbacks {
.onClick = [&, d](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& m) {
if(!l->mouseHovering && !l->childMouseHovering && !d.button->mouseHovering) {
if(m.down && d.clickAwayCallback) {
d.clickAwayCallback();
gui.set_to_layout();
}
if(!m.down && d.clickUpAwayCallback) {
d.clickUpAwayCallback();
gui.set_to_layout();
}
}
}
});

View file

@ -42,14 +42,25 @@ void window_fill_side_bar(GUIManager& gui, const WindowFillSideBarConfig& config
void window_gap_side_bar(GUIManager& gui, const WindowFillSideBarConfig::Direction& dir);
struct DropDownPopupLayout {
float dropdownOffset = 4.0f;
float popupOffset = 4.0f;
Element* button = nullptr;
bool* isOpen;
std::function<void()> clickAwayCallback;
std::function<void()> clickUpAwayCallback;
Vector2f entrySize;
size_t entryCount;
std::function<void(size_t i)> entryLayout;
};
struct AttachToButtonPopupLayout {
float popupOffset = 4.0f;
Element* button = nullptr;
std::function<void()> clickAwayCallback;
std::function<void()> clickUpAwayCallback;
Vector2f popupSize;
std::function<void()> innerLayout;
};
void attach_to_button_popup_layout(GUIManager& gui, const char* id, AttachToButtonPopupLayout d);
void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDownPopupLayout d);
}}

View file

@ -20,6 +20,7 @@
#include "../ElementHelpers/ColorPickerItemsHelpers.hpp"
#include "../ElementHelpers/ButtonHelpers.hpp"
#include "../ElementHelpers/LayoutHelpers.hpp"
#include "Helpers/ConvertVec.hpp"
namespace GUIStuff {
@ -28,7 +29,7 @@ template <typename T> ColorPickerButton<T>::ColorPickerButton(GUIManager& gui):
template <typename T> void ColorPickerButton<T>::layout(const Clay_ElementId& id, T* val, const ColorPickerButtonData& d) {
this->data = d;
GUIStuff::ElementHelpers::color_button(gui, "Color Picker Button", val, {
Element* button = GUIStuff::ElementHelpers::color_button(gui, "Color Picker Button", val, {
.isSelected = isOpen,
.hasAlpha = data.hasAlpha,
.onClick = [&]{
@ -38,18 +39,26 @@ template <typename T> void ColorPickerButton<T>::layout(const Clay_ElementId& id
});
if(isOpen) {
gui.set_z_index(gui.get_z_index() + 1, [&] {
ElementHelpers::top_to_bottom_window_popup_layout(gui, "Color Picker", CLAY_SIZING_FIT(300), CLAY_SIZING_FIT(0), [&, val, data = data](LayoutElement*) {
ElementHelpers::color_picker_items(gui, "c", val, {
.hasAlpha = data.hasAlpha,
.onEdit = data.onEdit,
.onSelect = data.onSelect,
.onDeselect = data.onDeselect
});
}, {
.mouseButton = [&](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {
if(!l->mouseHovering && button.button == InputManager::MouseButton::LEFT && button.down) {
isOpen = false;
gui.set_to_layout();
ElementHelpers::attach_to_button_popup_layout(gui, "Color Picker", {
.button = button,
.clickAwayCallback = [&] { isOpen = false; },
.popupSize = {300.0f, 300.0f},
.innerLayout = [&] {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.padding = CLAY_PADDING_ALL(gui.io.theme->padding1),
.childGap = gui.io.theme->childGap1,
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
}) {
ElementHelpers::color_picker_items(gui, "c", val, {
.forceAspectRatioOnColorPicker = false,
.hasAlpha = data.hasAlpha,
.onEdit = data.onEdit,
.onSelect = data.onSelect,
.onDeselect = data.onDeselect
});
}
}
});

View file

@ -94,7 +94,7 @@ template <typename T> class DropDown : public Element {
gui.set_z_index(gui.get_z_index() + 1, [&] {
dropdown_many_element_popup_layout(gui, "DROPDOWN", {
.button = this,
.isOpen = &isOpen,
.clickAwayCallback = [&] { isOpen = false; },
.entrySize = boundingBox.value().dim(),
.entryCount = selections.size(),
.entryLayout = [&](size_t i) {

View file

@ -22,7 +22,7 @@
DesktopDrawingProgramScreen::DesktopDrawingProgramScreen(MainProgram& m):
DrawingProgramScreen(m),
toolbar(m)
toolbar(m, *this)
{}
void DesktopDrawingProgramScreen::update() {
@ -76,3 +76,10 @@ void DesktopDrawingProgramScreen::input_text_key_callback(const InputManager::Ke
}
DrawingProgramScreen::input_text_key_callback(key);
}
void DesktopDrawingProgramScreen::open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName, bool isSaving) {
if(main.conf.useNativeFilePicker)
Screen::open_file_selector(filePickerName, extensionFilters, postSelectionFunc, fileName, isSaving);
else
toolbar.open_file_selector_non_native(filePickerName, extensionFilters, postSelectionFunc, fileName, isSaving);
}

View file

@ -28,6 +28,8 @@ class DesktopDrawingProgramScreen : public DrawingProgramScreen {
virtual bool app_close_requested() override;
virtual void input_key_callback(const InputManager::KeyCallbackArgs& key) override;
virtual void input_text_key_callback(const InputManager::KeyCallbackArgs& key) override;
virtual void open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false) override;
private:
Toolbar toolbar;
};

View file

@ -29,6 +29,8 @@
#include "../GUIStuff/Elements/SVGIcon.hpp"
#include "../GUIStuff/Elements/TextParagraph.hpp"
#include "../GUIStuff/ElementHelpers/LayoutHelpers.hpp"
#include "../GUIStuff/ElementHelpers/ColorPickerHelpers.hpp"
#include "../GUIStuff/ElementHelpers/CheckBoxHelpers.hpp"
#include "../GUIStuff/Elements/PositionAdjustingPopupMenu.hpp"
#include "../World.hpp"
#include "Helpers/StringHelpers.hpp"
@ -181,15 +183,17 @@ void FileSelectScreen::main_display() {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
},
}) {
file_view();
switch(selectedMenu) {
case SelectedMenu::FILES:
file_view();
if(!editMode)
create_file_button();
break;
case SelectedMenu::TRASH:
file_view();
break;
case SelectedMenu::SETTINGS:
settings_view();
break;
}
}
@ -479,51 +483,71 @@ void FileSelectScreen::title_bar() {
}
});
if(moreButton->get_bb().has_value() && moreOptionsMenu != MoreOptionsMenu::CLOSED) {
size_t elemCount = 0;
if(moreOptionsMenu == MoreOptionsMenu::MAIN)
elemCount = 2;
else if(moreOptionsMenu == MoreOptionsMenu::VIEW)
elemCount = 4;
gui.set_z_index(gui.get_z_index() + 10, [&] {
gui.element<PositionAdjustingPopupMenu>("more options popup", moreButton->get_bb().value().top_right(), [&] {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(200), .height = CLAY_SIZING_FIT(0)},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(gui.io.theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(4)
}) {
dropdown_many_element_popup_layout(gui, "more options popup", {
.button = moreButton,
.clickUpAwayCallback = [&] {
gui.set_post_callback_func_high_priority([&] { // Cancel actions on other buttons
moreOptionsMenu = MoreOptionsMenu::CLOSED;
main.g.gui.set_to_layout();
});
},
.entrySize = {200.0f, 30.0f},
.entryCount = elemCount,
.entryLayout = [&] (size_t i) {
if(moreOptionsMenu == MoreOptionsMenu::MAIN) {
text_transparent_option_button("Edit", "Edit", [&] {
moreOptionsMenu = MoreOptionsMenu::CLOSED;
start_edit_mode();
});
text_transparent_option_button("View", "View", [&] {
moreOptionsMenu = MoreOptionsMenu::VIEW;
});
switch(i) {
case 0: {
text_transparent_option_button("Edit", "Edit", [&] {
moreOptionsMenu = MoreOptionsMenu::CLOSED;
start_edit_mode();
});
break;
}
case 1: {
text_transparent_option_button("View", "View", [&] {
moreOptionsMenu = MoreOptionsMenu::VIEW;
});
break;
}
}
}
else if(moreOptionsMenu == MoreOptionsMenu::VIEW) {
text_transparent_option_button("Large Grid", "Large Grid", [&] {
fileViewType = FileViewType::LARGE_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
text_transparent_option_button("Medium Grid", "Medium Grid", [&] {
fileViewType = FileViewType::MEDIUM_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
text_transparent_option_button("Small Grid", "Small Grid", [&] {
fileViewType = FileViewType::SMALL_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
text_transparent_option_button("List", "List", [&] {
fileViewType = FileViewType::LIST;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
}
}
}, LayoutElement::Callbacks{
.onClick = [&](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& m) {
if(!l->mouseHovering && !m.down) {
gui.set_post_callback_func_high_priority([&] { // Cancel actions on other buttons
moreOptionsMenu = MoreOptionsMenu::CLOSED;
main.g.gui.set_to_layout();
});
switch(i) {
case 0: {
text_transparent_option_button("Large Grid", "Large Grid", [&] {
fileViewType = FileViewType::LARGE_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
break;
}
case 1: {
text_transparent_option_button("Medium Grid", "Medium Grid", [&] {
fileViewType = FileViewType::MEDIUM_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
break;
}
case 2: {
text_transparent_option_button("Small Grid", "Small Grid", [&] {
fileViewType = FileViewType::SMALL_GRID;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
break;
}
case 3: {
text_transparent_option_button("List", "List", [&] {
fileViewType = FileViewType::LIST;
moreOptionsMenu = MoreOptionsMenu::CLOSED;
});
break;
}
}
}
}
});
@ -541,6 +565,7 @@ void FileSelectScreen::text_transparent_option_button(const char* id, const char
text_button(gui, id, text, {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.wide = true,
.growHeight = true,
.centered = false,
.onClick = onClick
});
@ -569,7 +594,7 @@ void FileSelectScreen::main_menu() {
gui.element<LayoutElement>("inner main menu element", [&] (LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(300.0f * mainMenuOpenAnim->get_val()), .height = CLAY_SIZING_GROW(0)},
.sizing = {.width = CLAY_SIZING_FIXED(200.0f * mainMenuOpenAnim->get_val()), .height = CLAY_SIZING_GROW(0)},
.padding = CLAY_PADDING_ALL(gui.io.theme->padding1),
.layoutDirection = CLAY_TOP_TO_BOTTOM,
},
@ -579,18 +604,18 @@ void FileSelectScreen::main_menu() {
selectedMenu = SelectedMenu::FILES;
update_file_list(fileList, savePath, false);
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
if(mainViewScrollArea) mainViewScrollArea->reset_scroll();
});
icon_text_transparent_option_selected_button("Trash", "data/icons/trash.svg", "Trash", selectedMenu == SelectedMenu::TRASH, [&] {
selectedMenu = SelectedMenu::TRASH;
update_file_list(fileList, trashPath, true);
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
if(mainViewScrollArea) mainViewScrollArea->reset_scroll();
});
icon_text_transparent_option_selected_button("Settings", "data/icons/RemixIcon/settings-3-line.svg", "Settings", selectedMenu == SelectedMenu::SETTINGS, [&] {
selectedMenu = SelectedMenu::SETTINGS;
mainMenuOpenAnim->animation_trigger_reverse();
if(fileViewScrollArea) fileViewScrollArea->reset_scroll();
if(mainViewScrollArea) mainViewScrollArea->reset_scroll();
});
}
}
@ -602,11 +627,6 @@ void FileSelectScreen::main_menu() {
}
void FileSelectScreen::file_view() {
if(selectedMenu == SelectedMenu::SETTINGS) {
fileViewScrollArea = nullptr;
return;
}
auto& gui = main.g.gui;
std::filesystem::path folderPath = (selectedMenu == SelectedMenu::TRASH) ? trashPath : savePath;
@ -615,14 +635,21 @@ void FileSelectScreen::file_view() {
gui.element<SelectableButton>("file button", SelectableButton::Data{
.isSelected = editMode && fileList[i].selected,
.onClick = [&, filePath, i] {
if(editMode)
if(selectedMenu == SelectedMenu::TRASH) {
if(!editMode)
start_edit_mode();
fileList[i].selected = !fileList[i].selected;
}
else {
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
.isClient = false,
.saveThumbnail = true,
.filePathSource = filePath
});
if(editMode)
fileList[i].selected = !fileList[i].selected;
else {
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
.isClient = false,
.saveThumbnail = true,
.filePathSource = filePath
});
}
}
},
.innerContent = [&](const SelectableButton::InnerContentCallbackParameters& c){
@ -701,7 +728,7 @@ void FileSelectScreen::file_view() {
};
if(fileViewType == FileViewType::LIST) {
fileViewScrollArea = gui.element<ManyElementScrollArea>("File selector list", ManyElementScrollArea::Options{
mainViewScrollArea = gui.element<ManyElementScrollArea>("File selector list", ManyElementScrollArea::Options{
.entryHeight = 150.0f,
.entryCount = fileList.size(),
.clipHorizontal = true,
@ -730,7 +757,7 @@ void FileSelectScreen::file_view() {
default:
break;
}
fileViewScrollArea = gui.element<GridScrollArea>("File selector grid", GridScrollArea::Options{
mainViewScrollArea = gui.element<GridScrollArea>("File selector grid", GridScrollArea::Options{
.entryWidth = entrySize.x(),
.childAlignmentX = CLAY_ALIGN_X_LEFT,
.entryHeight = entrySize.y(),
@ -743,6 +770,40 @@ void FileSelectScreen::file_view() {
}
}
void FileSelectScreen::settings_view() {
auto& gui = main.g.gui;
mainViewScrollArea = gui.element<ScrollArea>("settings scroll area", ScrollArea::Options{
.scrollVertical = true,
.clipVertical = true,
.scrollbarY = ScrollArea::ScrollbarType::NORMAL,
.innerContent = [&] (auto&) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0, 300), .height = CLAY_SIZING_GROW(0)},
.childGap = gui.io.theme->childGap1,
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
}) {
color_picker_button_field(gui, "defaultCanvasBackgroundColor", "Default canvas background color", &main.conf.defaultCanvasBackgroundColor, { .hasAlpha = false });
input_scalar_field(gui, "Max GUI Scale", "Max GUI Scale", &main.conf.guiScale, 1.0f, 2.0f, {
.decimalPrecision = 1,
.onEdit = [&] { main.g.window_update(); }
});
input_scalar_field(gui, "jump transition time", "Jump transition time", &main.conf.jumpTransitionTime, 0.01f, 1000.0f, {.decimalPrecision = 2});
checkbox_boolean_field(gui, "make all tools share same size", "Make all tools share size", &main.toolConfig.globalConf.useGlobalRelativeWidth);
}
}
}
});
}
void FileSelectScreen::menu_black_box() {
auto& gui = main.g.gui;
if(!mainMenuOpenAnim->is_at_start()) {

View file

@ -63,11 +63,12 @@ class FileSelectScreen : public Screen {
GUIStuff::GUIFloatAnimation* mainMenuOpenAnim = nullptr;
GUIStuff::GUIFloatAnimation* actionBarOpenAnim = nullptr;
GUIStuff::ScrollArea* fileViewScrollArea = nullptr;
GUIStuff::ScrollArea* mainViewScrollArea = nullptr;
void main_display();
void main_menu();
void file_view();
void settings_view();
void create_file_button();
void file_view_edit();
void menu_black_box();

View file

@ -174,6 +174,20 @@ void PhoneDrawingProgramScreen::top_toolbar_remaining_area() {
void PhoneDrawingProgramScreen::top_toolbar_hidden_button_popup(GUIStuff::Element* b, std::vector<TopToolbarRemainingAreaButton> l) {
auto& gui = main.g.gui;
l.emplace_back(TopToolbarRemainingAreaButton{
.name = "Add Image",
.svgPath = "data/icons/RemixIcon/image-add-line.svg",
.onClick = [&] {
open_file_selector("Open File", {{"Any File", "*"}}, [&](const std::filesystem::path& p, const auto& e) {
CustomEvents::emit_event<CustomEvents::AddFileToCanvasEvent>({
.type = CustomEvents::AddFileToCanvasEvent::Type::PATH,
.filePath = p,
.pos = main.window.size.cast<float>() / 2.0f
});
});
}
});
l.emplace_back(TopToolbarRemainingAreaButton{
.name = "Canvas Color",
.svgPath = "data/icons/RemixIcon/settings-3-line.svg",
@ -191,7 +205,7 @@ void PhoneDrawingProgramScreen::top_toolbar_hidden_button_popup(GUIStuff::Elemen
gui.set_z_index(gui.get_z_index() + 1, [&] {
dropdown_many_element_popup_layout(gui, "top toolbar hidden button popup", {
.button = b,
.isOpen = &topToolbarButtonPopupOpen,
.clickAwayCallback = [&] { topToolbarButtonPopupOpen = false; },
.entrySize = {150.0f, 30.0f},
.entryCount = l.size(),
.entryLayout = [&] (size_t i) {

View file

@ -17,11 +17,45 @@
*/
#include "Screen.hpp"
#include "../MainProgram.hpp"
Screen::Screen(MainProgram& m):
main(m)
{}
Screen::NativeFilePicker Screen::nativeFilePicker;
// You can't trust that filter wont be -1 on the platform youre on, so dont use the extension from the callback
void Screen::sdl_open_file_dialog_callback(void* userData, const char * const * fileList, int filter) {
if(!fileList) {
nativeFilePicker.isOpen = false;
return;
}
if(!(*fileList)) {
nativeFilePicker.isOpen = false;
return;
}
ExtensionFilter e;
if(filter >= 0)
e = nativeFilePicker.extensionFiltersComplete[filter];
nativeFilePicker.postSelectionFunc(fileList[0], e);
nativeFilePicker.isOpen = false;
}
void Screen::open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName, bool isSaving) {
if(!nativeFilePicker.isOpen) {
nativeFilePicker.postSelectionFunc = postSelectionFunc;
nativeFilePicker.extensionFiltersComplete = extensionFilters;
nativeFilePicker.sdlFileFilters.clear();
for(auto& e : nativeFilePicker.extensionFiltersComplete)
nativeFilePicker.sdlFileFilters.emplace_back(e.name.c_str(), e.extensions.c_str());
if(isSaving)
SDL_ShowSaveFileDialog(sdl_open_file_dialog_callback, nullptr, main.window.sdlWindow, nativeFilePicker.sdlFileFilters.data(), nativeFilePicker.sdlFileFilters.size(), nullptr);
else
SDL_ShowOpenFileDialog(sdl_open_file_dialog_callback, nullptr, main.window.sdlWindow, nativeFilePicker.sdlFileFilters.data(), nativeFilePicker.sdlFileFilters.size(), nullptr, false);
}
}
void Screen::update() {}
void Screen::gui_layout_run() {}
bool Screen::app_close_requested() { return true; }

View file

@ -25,6 +25,20 @@ class Screen {
virtual void update();
virtual void draw(SkCanvas* canvas) = 0;
struct ExtensionFilter {
std::string name;
std::string extensions;
};
typedef std::function<void(const std::filesystem::path&, const ExtensionFilter& extensionSelected)> OpenFileSelectorCallback;
struct NativeFilePicker {
std::atomic<bool> isOpen = false;
std::vector<ExtensionFilter> extensionFiltersComplete;
std::vector<SDL_DialogFileFilter> sdlFileFilters;
OpenFileSelectorCallback postSelectionFunc;
};
static NativeFilePicker nativeFilePicker;
virtual void gui_layout_run();
virtual bool app_close_requested();
virtual void input_add_file_to_canvas_callback(const CustomEvents::AddFileToCanvasEvent& addFile);
@ -53,7 +67,11 @@ class Screen {
virtual void input_app_about_to_go_to_background_callback();
virtual std::optional<InputManager::TextBoxStartInfo> get_text_box_start_info();
virtual void open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
virtual ~Screen();
protected:
static void sdl_open_file_dialog_callback(void* userData, const char * const * fileList, int filter);
MainProgram& main;
};

View file

@ -71,6 +71,8 @@
#include "GUIStuff/Elements/MovableTabList.hpp"
#include "GUIStuff/Elements/TextParagraph.hpp"
#include "Screens/DesktopDrawingProgramScreen.hpp"
#define UPDATE_DOWNLOAD_URL "https://infinipaint.com/download.html"
#define UPDATE_NOTIFICATION_URL "https://infinipaint.com/updateNotificationVersion.txt"
@ -78,60 +80,31 @@
#include <EmscriptenHelpers/emscripten_browser_file.h>
#endif
Toolbar::NativeFilePicker Toolbar::nativeFilePicker;
using namespace GUIStuff;
using namespace ElementHelpers;
Toolbar::Toolbar(MainProgram& initMain):
main(initMain)
Toolbar::Toolbar(MainProgram& initMain, DesktopDrawingProgramScreen& initDrawScreen):
main(initMain),
drawScreen(initDrawScreen)
{}
// You can't trust that filter wont be -1 on the platform youre on, so dont use the extension from the callback
void Toolbar::sdl_open_file_dialog_callback(void* userData, const char * const * fileList, int filter) {
if(!fileList) {
nativeFilePicker.isOpen = false;
return;
}
if(!(*fileList)) {
nativeFilePicker.isOpen = false;
return;
}
ExtensionFilter e;
if(filter >= 0)
e = nativeFilePicker.extensionFiltersComplete[filter];
nativeFilePicker.postSelectionFunc(fileList[0], e);
nativeFilePicker.isOpen = false;
void Toolbar::open_file_selector(const std::string& filePickerName, const std::vector<Screen::ExtensionFilter>& extensionFilters, Screen::OpenFileSelectorCallback postSelectionFunc, const std::string& fileName, bool isSaving) {
drawScreen.open_file_selector(filePickerName, extensionFilters, postSelectionFunc, fileName, isSaving);
}
void Toolbar::open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName, bool isSaving) {
if(main.conf.useNativeFilePicker) {
if(!nativeFilePicker.isOpen) {
nativeFilePicker.postSelectionFunc = postSelectionFunc;
nativeFilePicker.extensionFiltersComplete = extensionFilters;
nativeFilePicker.sdlFileFilters.clear();
for(auto& e : nativeFilePicker.extensionFiltersComplete)
nativeFilePicker.sdlFileFilters.emplace_back(e.name.c_str(), e.extensions.c_str());
if(isSaving)
SDL_ShowSaveFileDialog(sdl_open_file_dialog_callback, nullptr, main.window.sdlWindow, nativeFilePicker.sdlFileFilters.data(), nativeFilePicker.sdlFileFilters.size(), nullptr);
else
SDL_ShowOpenFileDialog(sdl_open_file_dialog_callback, nullptr, main.window.sdlWindow, nativeFilePicker.sdlFileFilters.data(), nativeFilePicker.sdlFileFilters.size(), nullptr, false);
}
}
else {
filePicker.isOpen = true;
filePicker.extensionFiltersComplete = extensionFilters;
filePicker.extensionFilters.clear();
for(auto& [name, exList] : extensionFilters)
filePicker.extensionFilters.emplace_back(exList);
filePicker.extensionSelected = extensionFilters.size() - 1;
filePicker.filePickerWindowName = filePickerName;
filePicker.postSelectionFunc = postSelectionFunc;
filePicker.fileName = "";
filePicker.isSaving = isSaving;
filePicker.entriesScrollArea = nullptr;
file_picker_gui_refresh_entries();
}
void Toolbar::open_file_selector_non_native(const std::string& filePickerName, const std::vector<Screen::ExtensionFilter>& extensionFilters, Screen::OpenFileSelectorCallback postSelectionFunc, const std::string& fileName, bool isSaving) {
filePicker.isOpen = true;
filePicker.extensionFiltersComplete = extensionFilters;
filePicker.extensionFilters.clear();
for(auto& [name, exList] : extensionFilters)
filePicker.extensionFilters.emplace_back(exList);
filePicker.extensionSelected = extensionFilters.size() - 1;
filePicker.filePickerWindowName = filePickerName;
filePicker.postSelectionFunc = postSelectionFunc;
filePicker.fileName = "";
filePicker.isSaving = isSaving;
filePicker.entriesScrollArea = nullptr;
file_picker_gui_refresh_entries();
}
void Toolbar::color_button_left(const char* id, Vector4f* color, const ColorSelectorButtonData& colorSelectorData) {
@ -1781,10 +1754,11 @@ void Toolbar::general_settings_inner_gui() {
if(!themeData.selectedThemeIndex)
reload_theme_list();
CLAY_AUTO_ID({.layout = {
.childGap = io.theme->childGap1,
.childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT,
CLAY_AUTO_ID({.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0)},
.childGap = io.theme->childGap1,
.childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT,
}
}) {
text_label(gui, "Theme: ");

View file

@ -27,8 +27,10 @@
#include <Helpers/Serializers.hpp>
#include <SDL3/SDL_dialog.h>
#include <Helpers/VersionNumber.hpp>
#include "Screens/Screen.hpp"
class MainProgram;
class DesktopDrawingProgramScreen;
class Toolbar {
public:
@ -44,14 +46,9 @@ class Toolbar {
TimePoint time;
};
struct ExtensionFilter {
std::string name;
std::string extensions;
};
std::string chatMessageInput;
Toolbar(MainProgram& initMain);
Toolbar(MainProgram& initMain, DesktopDrawingProgramScreen& initDrawScreen);
void update();
void layout_run();
@ -74,8 +71,8 @@ class Toolbar {
void paint_popup(Vector2f popupPos);
typedef std::function<void(const std::filesystem::path&, const ExtensionFilter& extensionSelected)> OpenFileSelectorCallback;
void open_file_selector(const std::string& filePickerName, const std::vector<ExtensionFilter>& extensionFilters, OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
void open_file_selector(const std::string& filePickerName, const std::vector<Screen::ExtensionFilter>& extensionFilters, Screen::OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
void open_file_selector_non_native(const std::string& filePickerName, const std::vector<Screen::ExtensionFilter>& extensionFilters, Screen::OpenFileSelectorCallback postSelectionFunc, const std::string& fileName = "", bool isSaving = false);
void save_func();
void save_as_func();
@ -203,7 +200,7 @@ class Toolbar {
struct FilePicker {
bool isOpen = false;
std::string filePickerWindowName;
std::vector<ExtensionFilter> extensionFiltersComplete;
std::vector<Screen::ExtensionFilter> extensionFiltersComplete;
std::vector<std::string> extensionFilters;
std::vector<std::filesystem::path> entries;
std::filesystem::path currentSelectedPath;
@ -211,17 +208,9 @@ class Toolbar {
bool isSaving = false;
size_t extensionSelected;
GUIStuff::ScrollArea* entriesScrollArea = nullptr;
OpenFileSelectorCallback postSelectionFunc;
Screen::OpenFileSelectorCallback postSelectionFunc;
} filePicker;
struct NativeFilePicker {
std::atomic<bool> isOpen = false;
std::vector<ExtensionFilter> extensionFiltersComplete;
std::vector<SDL_DialogFileFilter> sdlFileFilters;
OpenFileSelectorCallback postSelectionFunc;
};
static NativeFilePicker nativeFilePicker;
RichText::TextData build_paragraph_from_chat_message(const ChatMessage& message, float alpha);
void load_icons_at(const std::filesystem::path& pathToLoad);
@ -230,4 +219,5 @@ class Toolbar {
std::string serverLocalID;
MainProgram& main;
DesktopDrawingProgramScreen& drawScreen;
};