diff -r 88bc21b19213 -r 097f45f9c1fa ui/winui/text.cpp --- a/ui/winui/text.cpp Fri Oct 20 16:34:33 2023 +0200 +++ b/ui/winui/text.cpp Sun Nov 26 15:44:28 2023 +0100 @@ -33,16 +33,23 @@ #include "../common/context.h" #include "../common/object.h" +#include +#include + #include "util.h" #include "container.h" + + using namespace winrt; using namespace Microsoft::UI::Xaml; using namespace Microsoft::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Interop; using namespace winrt::Windows::Foundation; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Microsoft::UI::Xaml::Media; using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; - +using namespace winrt::Windows::UI::Xaml::Input; UIWIDGET ui_textfield_create(UiObject* obj, UiTextFieldArgs args) { UiObject* current = uic_current_obj(obj); @@ -160,3 +167,303 @@ PasswordBox box = widget->uielement.as(); box.Password(ui_string_set(str, newvalue)); } + + +// ------------------------ path textfield -------------------------------------- + +extern "C" static void destroy_ui_pathtextfield(void* ptr) { + UiPathTextField* pb = (UiPathTextField*)ptr; + delete pb; +} + +static void ui_context_add_pathtextfield_destructor(UiContext* ctx, UiPathTextField* pb) { + cxMempoolRegister(ctx->mp, pb, destroy_ui_pathtextfield); +} + +static void ui_pathtextfield_clear(StackPanel& buttons) { + for (int i = buttons.Children().Size() - 1; i >= 0; i--) { + buttons.Children().RemoveAt(i); + } +} + +static void ui_pathfield_free_pathelms(UiPathElm* elms, size_t nelm) { + if (!elms) { + return; + } + for (int i = 0; i < nelm; i++) { + UiPathElm e = elms[i]; + free(e.name); + free(e.path); + } + free(elms); +} + +UiPathTextField::~UiPathTextField() { + ui_pathfield_free_pathelms(this->current_path, this->current_path_nelms); +} + +static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return nullptr; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} + +void ui_pathtextfield_update(UiPathTextField* pb, const char *full_path) { + Grid grid = pb->grid; + + ui_pathelm_func getpathelm = pb->getpathelm; + void* getpathelmdata = pb->getpathelmdata; + + // hide textbox, show button panel + pb->textbox.Visibility(Visibility::Collapsed); + pb->buttons.Visibility(Visibility::Visible); + + // clear old buttons + ui_pathtextfield_clear(pb->buttons); + + size_t full_path_len = full_path ? strlen(full_path) : 0; + + size_t nelm = 0; + UiPathElm* path_elm = getpathelm(full_path, full_path_len, &nelm, getpathelmdata); + ui_pathfield_free_pathelms(pb->current_path, pb->current_path_nelms); + pb->current_path = path_elm; + pb->current_path_nelms = nelm; + + // add new buttons + int j = 0; + for (int i = 0; i < nelm;i++) { + UiPathElm elm = path_elm[i]; + wchar_t* wstr = str2wstr_len(elm.name, elm.name_len, nullptr); + Button button = Button(); + button.Content(box_value(wstr)); + free(wstr); + + if (pb->onactivate) { + button.Click([pb, j, elm](IInspectable const& sender, RoutedEventArgs) { + // copy elm.path because it could be a non-terminated string + cxmutstr elmpath = cx_strdup(cx_strn(elm.path, elm.path_len)); + + UiEvent evt; + evt.obj = pb->obj; + evt.window = evt.obj->window; + evt.document = evt.obj->ctx->document; + evt.eventdata = elmpath.ptr; + evt.intval = j; + pb->onactivate(&evt, pb->onactivatedata); + + free(elmpath.ptr); + }); + } + + Thickness t = { 0, 0, 1, 0 }; + CornerRadius c = { 0 ,0, 0, 0 }; + button.BorderThickness(t); + button.CornerRadius(c); + + pb->buttons.Children().Append(button); + + j++; + } +} + +char* ui_path_textfield_get(UiString * str) { + UiPathTextField* widget = (UiPathTextField*)str->obj; + TextBox box = widget->textbox; + std::wstring wstr(box.Text()); + return ui_string_get(str, wstr); +} + +void ui_path_textfield_set(UiString* str, const char* newvalue) { + UiPathTextField* widget = (UiPathTextField*)str->obj; + TextBox box = widget->textbox; + box.Text(ui_string_set(str, newvalue)); + ui_pathtextfield_update(widget, newvalue); +} + +UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + // create view and toolkit wrapper + Border pathbar = Border(); + + IInspectable bgRes = Application::Current().Resources().Lookup(box_value(L"TextControlBackground")); + IInspectable borderThicknessRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderThemeThickness")); + IInspectable borderBrushRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderBrush")); + // IInspectable cornerRes = Application::Current().Resources().Lookup(box_value(L"TextControlCornerRadius")); + + Brush bgBrush = unbox_value(bgRes); + Thickness border = unbox_value(borderThicknessRes); + Brush borderBrush = unbox_value(borderBrushRes); + CornerRadius cornerRadius = { 4, 4, 4, 4 }; //unbox_value(cornerRes); + + pathbar.Background(bgBrush); + pathbar.BorderBrush(borderBrush); + pathbar.BorderThickness(border); + pathbar.CornerRadius(cornerRadius); + + Grid content = Grid(); + pathbar.Child(content); + + GridLength gl; + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + content.ColumnDefinitions().Append(coldef); + + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + + ColumnDefinition coldef2 = ColumnDefinition(); + coldef2.Width(gl); + content.ColumnDefinitions().Append(coldef2); + + TextBox pathTextBox = TextBox(); + Thickness t = { 0, 0, 0, 0 }; + CornerRadius c = { 0 ,0, 0, 0 }; + pathTextBox.BorderThickness(t); + //pathTextBox.CornerRadius(c); + + + pathTextBox.HorizontalAlignment(HorizontalAlignment::Stretch); + content.SetColumn(pathTextBox, 0); + content.SetColumnSpan(pathTextBox, 2); + + content.Children().Append(pathTextBox); + + // stackpanel for buttons + StackPanel buttons = StackPanel(); + buttons.Orientation(Orientation::Horizontal); + buttons.Visibility(Visibility::Collapsed); + content.SetColumn(buttons, 0); + content.Children().Append(buttons); + + TextBlock filler = TextBlock(); + filler.VerticalAlignment(VerticalAlignment::Stretch); + //filler.Text(winrt::hstring(L"hello filler")); + + filler.HorizontalAlignment(HorizontalAlignment::Stretch); + filler.VerticalAlignment(VerticalAlignment::Stretch); + content.SetColumn(filler, 1); + content.Children().Append(filler); + + filler.PointerPressed( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + pathTextBox.Visibility(Visibility::Visible); + buttons.Visibility(Visibility::Collapsed); + filler.Visibility(Visibility::Collapsed); + pathTextBox.SelectionStart(pathTextBox.Text().size()); + pathTextBox.SelectionLength(0); + pathTextBox.Focus(FocusState::Keyboard); + }) + ); + + //pathTextBox.Visibility(Visibility::Collapsed); + + UiPathTextField* uipathbar = new UiPathTextField; + ui_context_add_pathtextfield_destructor(current->ctx, uipathbar); + uipathbar->grid = content; + uipathbar->buttons = buttons; + uipathbar->textbox = pathTextBox; + uipathbar->filler = filler; + uipathbar->obj = obj; + uipathbar->getpathelm = args.getpathelm ? args.getpathelm : default_pathelm_func; + uipathbar->getpathelmdata = args.getpathelmdata; + uipathbar->onactivate = args.onactivate; + uipathbar->onactivatedata = args.onactivatedata; + uipathbar->ondragstart = args.ondragstart; + uipathbar->ondragstartdata = args.ondragstartdata; + uipathbar->ondragcomplete = args.ondragcomplete; + uipathbar->ondragcompletedata = args.ondragcompletedata; + uipathbar->ondrop = args.ondrop; + uipathbar->ondropdata = args.ondropsdata; + + + pathTextBox.KeyDown( + winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( + [=](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) { + auto key = e.Key(); + bool showButtons = false; + bool update = false; + if (key == Windows::System::VirtualKey::Escape) { + showButtons = true; + } + else if (key == Windows::System::VirtualKey::Enter) { + showButtons = true; + update = true; + } + + if (showButtons) { + pathTextBox.Visibility(Visibility::Collapsed); + buttons.Visibility(Visibility::Visible); + filler.Visibility(Visibility::Visible); + if (update) { + std::wstring value(pathTextBox.Text()); + char* full_path = wchar2utf8(value.c_str(), value.length()); + ui_pathtextfield_update(uipathbar, full_path); + free(full_path); + } + + //buttons.Focus(FocusState::Keyboard); + } + }) + ); + + + UIElement elm = pathbar; + UiWidget* widget = new UiWidget(elm); + widget->data1 = uipathbar; + ui_context_add_widget_destructor(current->ctx, widget); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_LIST); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = uipathbar; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(pathbar, false); + + return widget; +}