UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2023 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "pch.h" 30 31 #include "text.h" 32 33 #include "../common/context.h" 34 #include "../common/object.h" 35 36 #include <cx/string.h> 37 #include <cx/allocator.h> 38 39 #include "util.h" 40 #include "container.h" 41 42 43 44 using namespace winrt; 45 using namespace Microsoft::UI::Xaml; 46 using namespace Microsoft::UI::Xaml::Controls; 47 using namespace Windows::UI::Xaml::Interop; 48 using namespace winrt::Windows::Foundation; 49 using namespace Microsoft::UI::Xaml::Markup; 50 using namespace Microsoft::UI::Xaml::Media; 51 using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; 52 using namespace winrt::Windows::UI::Xaml::Input; 53 54 55 UIEXPORT UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { 56 UiObject* current = uic_current_obj(obj); 57 58 // create textarea and toolkit wrapper 59 TextBox textarea = TextBox(); 60 textarea.AcceptsReturn(true); 61 ScrollViewer::SetVerticalScrollBarVisibility(textarea, ScrollBarVisibility::Auto); 62 UIElement elm = textarea; 63 UiWidget* widget = new UiWidget(elm); 64 ui_context_add_widget_destructor(current->ctx, widget); 65 ui_set_widget_groups(current->ctx, widget, args.groups); 66 67 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_TEXT); 68 if (var) { 69 UiText* value = (UiText*)var->value; 70 value->obj = widget; 71 value->set = ui_textarea_set; 72 value->get = ui_textarea_get; 73 value->getsubstr = ui_textarea_getsubstr; 74 value->insert = ui_textarea_insert; 75 value->setposition = ui_textarea_setposition; 76 value->position = ui_textarea_position; 77 value->selection = ui_textarea_selection; 78 value->length = ui_textarea_length; 79 value->remove = ui_textarea_remove; 80 } 81 82 // add textarea to current container 83 UI_APPLY_LAYOUT1(current, args); 84 85 current->container->Add(textarea, true); 86 87 return widget; 88 } 89 90 UIEXPORT UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { 91 return textarea; 92 } 93 94 UIEXPORT void ui_text_undo(UiText *value) { 95 96 } 97 98 UIEXPORT void ui_text_redo(UiText *value) { 99 100 } 101 102 // -------------------------- getter/setter for textarea UiText -------------------------- 103 104 char* ui_wtext_get(UiText *text, std::wstring &value) { 105 if (text->value.ptr) { 106 text->value.free(text->value.ptr); 107 } 108 109 text->value.ptr = wchar2utf8(value.c_str(), value.length()); 110 text->value.free = free; 111 112 return text->value.ptr; 113 } 114 115 std::wstring ui_wtext_set(UiText *text, const char* value) { 116 if (text->value.ptr) { 117 text->value.free(text->value.ptr); 118 } 119 120 text->value.ptr = _strdup(value); 121 text->value.free = free; 122 123 int len; 124 wchar_t* wstr = str2wstr(value, &len); 125 std::wstring s(wstr); 126 free(wstr); 127 128 return s; 129 } 130 131 extern "C" char* ui_textarea_get(UiText *text) { 132 UiWidget* widget = (UiWidget*)text->obj; 133 TextBox box = widget->uielement.as<TextBox>(); 134 std::wstring wstr(box.Text()); 135 return ui_wtext_get(text, wstr); 136 } 137 138 extern "C" void ui_textarea_set(UiText *text, const char *newvalue) { 139 UiWidget* widget = (UiWidget*)text->obj; 140 TextBox box = widget->uielement.as<TextBox>(); 141 box.Text(ui_wtext_set(text, newvalue)); 142 } 143 144 extern "C" char* ui_textarea_getsubstr(UiText *text, int begin, int end) { 145 return NULL; 146 } 147 148 extern "C" void ui_textarea_insert(UiText *text, int pos, char *str) { 149 150 } 151 152 extern "C" void ui_textarea_setposition(UiText *text, int pos) { 153 154 } 155 156 extern "C" int ui_textarea_position(UiText *text) { 157 return 0; 158 } 159 160 extern "C" void ui_textarea_selection(UiText *text, int *begin, int *end) { 161 162 } 163 164 extern "C" int ui_textarea_length(UiText *text) { 165 return 0; 166 } 167 168 extern "C" void ui_textarea_remove(UiText *text, int begin, int end) { 169 170 } 171 172 173 174 175 UIWIDGET ui_textfield_create(UiObject* obj, UiTextFieldArgs args) { 176 UiObject* current = uic_current_obj(obj); 177 178 // create textbox and toolkit wrapper 179 TextBox textfield = TextBox(); 180 UIElement elm = textfield; 181 UiWidget* widget = new UiWidget(elm); 182 ui_context_add_widget_destructor(current->ctx, widget); 183 ui_set_widget_groups(current->ctx, widget, args.groups); 184 185 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 186 if (var) { 187 UiString* value = (UiString*)var->value; 188 value->obj = widget; 189 value->get = ui_textfield_get; 190 value->set = ui_textfield_set; 191 192 // listener for notifying observers 193 // TODO: 194 } 195 196 // add textfield to current container 197 UI_APPLY_LAYOUT1(current, args); 198 199 current->container->Add(textfield, false); 200 201 return widget; 202 } 203 204 UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { 205 return ui_textfield_create(obj, args); 206 } 207 208 UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { 209 UiObject* current = uic_current_obj(obj); 210 211 // create textbox and toolkit wrapper 212 PasswordBox textfield = PasswordBox(); 213 UIElement elm = textfield; 214 UiWidget* widget = new UiWidget(elm); 215 ui_context_add_widget_destructor(current->ctx, widget); 216 ui_set_widget_groups(current->ctx, widget, args.groups); 217 218 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 219 if (var) { 220 UiString* value = (UiString*)var->value; 221 value->obj = widget; 222 value->get = ui_passwordfield_get; 223 value->set = ui_passwordfield_set; 224 225 // listener for notifying observers 226 // TODO: 227 } 228 229 // add textfield to current container 230 UI_APPLY_LAYOUT1(current, args); 231 232 current->container->Add(textfield, false); 233 234 return widget; 235 } 236 237 238 // -------------------------- getter/setter for textfield UiString -------------------------- 239 240 char* ui_wstring_get(UiString* str, std::wstring &value) { 241 if (str->value.ptr) { 242 str->value.free(str->value.ptr); 243 } 244 245 str->value.ptr = wchar2utf8(value.c_str(), value.length()); 246 str->value.free = free; 247 248 return str->value.ptr; 249 } 250 251 std::wstring ui_wstring_set(UiString* str, const char* value) { 252 if (str->value.ptr) { 253 str->value.free(str->value.ptr); 254 } 255 256 str->value.ptr = _strdup(value); 257 str->value.free = free; 258 259 int len; 260 wchar_t* wstr = str2wstr(value, &len); 261 std::wstring s(wstr); 262 free(wstr); 263 264 return s; 265 } 266 267 char* ui_textfield_get(UiString * str) { 268 UiWidget* widget = (UiWidget*)str->obj; 269 TextBox box = widget->uielement.as<TextBox>(); 270 std::wstring wstr(box.Text()); 271 return ui_wstring_get(str, wstr); 272 } 273 274 void ui_textfield_set(UiString * str, const char* newvalue) { 275 UiWidget* widget = (UiWidget*)str->obj; 276 TextBox box = widget->uielement.as<TextBox>(); 277 box.Text(ui_wstring_set(str, newvalue)); 278 } 279 280 281 char* ui_passwordfield_get(UiString * str) { 282 UiWidget* widget = (UiWidget*)str->obj; 283 PasswordBox box = widget->uielement.as<PasswordBox>(); 284 std::wstring wstr(box.Password()); 285 return ui_wstring_get(str, wstr); 286 } 287 288 void ui_passwordfield_set(UiString * str, const char* newvalue) { 289 UiWidget* widget = (UiWidget*)str->obj; 290 PasswordBox box = widget->uielement.as<PasswordBox>(); 291 box.Password(ui_wstring_set(str, newvalue)); 292 } 293 294 295 // ------------------------ path textfield -------------------------------------- 296 297 extern "C" static void destroy_ui_pathtextfield(void* ptr) { 298 UiPathTextField* pb = (UiPathTextField*)ptr; 299 delete pb; 300 } 301 302 static void ui_context_add_pathtextfield_destructor(UiContext* ctx, UiPathTextField* pb) { 303 cxMempoolRegister(ctx->mp, pb, destroy_ui_pathtextfield); 304 } 305 306 static void ui_pathtextfield_clear(StackPanel& buttons) { 307 for (int i = buttons.Children().Size() - 1; i >= 0; i--) { 308 buttons.Children().RemoveAt(i); 309 } 310 } 311 312 static void ui_pathfield_free_pathelms(UiPathElm* elms, size_t nelm) { 313 if (!elms) { 314 return; 315 } 316 for (int i = 0; i < nelm; i++) { 317 UiPathElm e = elms[i]; 318 free(e.name); 319 free(e.path); 320 } 321 free(elms); 322 } 323 324 UiPathTextField::~UiPathTextField() { 325 ui_pathfield_free_pathelms(this->current_path, this->current_path_nelms); 326 } 327 328 static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { 329 cxstring *pathelms; 330 size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); 331 332 if (nelm == 0) { 333 *ret_nelm = 0; 334 return nullptr; 335 } 336 337 UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); 338 size_t n = nelm; 339 int j = 0; 340 for (int i = 0; i < nelm; i++) { 341 cxstring c = pathelms[i]; 342 if (c.length == 0) { 343 if (i == 0) { 344 c.length = 1; 345 } 346 else { 347 n--; 348 continue; 349 } 350 } 351 352 cxmutstr m = cx_strdup(c); 353 elms[j].name = m.ptr; 354 elms[j].name_len = m.length; 355 356 size_t elm_path_len = c.ptr + c.length - full_path; 357 cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); 358 elms[j].path = elm_path.ptr; 359 elms[j].path_len = elm_path.length; 360 361 j++; 362 } 363 *ret_nelm = n; 364 365 return elms; 366 } 367 368 int ui_pathtextfield_update(UiPathTextField* pb, const char *full_path) { 369 Grid grid = pb->grid; 370 371 ui_pathelm_func getpathelm = pb->getpathelm; 372 void* getpathelmdata = pb->getpathelmdata; 373 374 size_t full_path_len = full_path ? strlen(full_path) : 0; 375 376 size_t nelm = 0; 377 UiPathElm* path_elm = getpathelm(full_path, full_path_len, &nelm, getpathelmdata); 378 if (!path_elm) { 379 return 1; 380 } 381 382 // hide textbox, show button panel 383 pb->textbox.Visibility(Visibility::Collapsed); 384 pb->buttons.Visibility(Visibility::Visible); 385 386 // clear old buttons 387 ui_pathtextfield_clear(pb->buttons); 388 389 ui_pathfield_free_pathelms(pb->current_path, pb->current_path_nelms); 390 pb->current_path = path_elm; 391 pb->current_path_nelms = nelm; 392 393 // add new buttons 394 int j = 0; 395 for (int i = 0; i < nelm;i++) { 396 UiPathElm elm = path_elm[i]; 397 wchar_t* wstr = str2wstr_len(elm.name, elm.name_len, nullptr); 398 Button button = Button(); 399 button.Content(box_value(wstr)); 400 free(wstr); 401 402 if (pb->onactivate) { 403 button.Click([pb, j, elm](IInspectable const& sender, RoutedEventArgs) { 404 // copy elm.path because it could be a non-terminated string 405 cxmutstr elmpath = cx_strdup(cx_strn(elm.path, elm.path_len)); 406 407 UiEvent evt; 408 evt.obj = pb->obj; 409 evt.window = evt.obj->window; 410 evt.document = evt.obj->ctx->document; 411 evt.eventdata = elmpath.ptr; 412 evt.intval = j; 413 pb->onactivate(&evt, pb->onactivatedata); 414 415 free(elmpath.ptr); 416 }); 417 } 418 419 Thickness t = { 0, 0, 1, 0 }; 420 CornerRadius c = { 0 ,0, 0, 0 }; 421 button.BorderThickness(t); 422 button.CornerRadius(c); 423 424 pb->buttons.Children().Append(button); 425 426 j++; 427 } 428 429 return 0; 430 } 431 432 char* ui_path_textfield_get(UiString * str) { 433 UiPathTextField* widget = (UiPathTextField*)str->obj; 434 TextBox box = widget->textbox; 435 std::wstring wstr(box.Text()); 436 return ui_wstring_get(str, wstr); 437 } 438 439 void ui_path_textfield_set(UiString* str, const char* newvalue) { 440 UiPathTextField* widget = (UiPathTextField*)str->obj; 441 TextBox box = widget->textbox; 442 box.Text(ui_wstring_set(str, newvalue)); 443 ui_pathtextfield_update(widget, newvalue); 444 } 445 446 UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { 447 UiObject* current = uic_current_obj(obj); 448 449 // create view and toolkit wrapper 450 Border pathbar = Border(); 451 452 IInspectable bgRes = Application::Current().Resources().Lookup(box_value("TextControlBackground"L)); 453 IInspectable borderThicknessRes = Application::Current().Resources().Lookup(box_value("TextControlBorderThemeThickness"L)); 454 IInspectable borderBrushRes = Application::Current().Resources().Lookup(box_value("TextControlBorderBrush"L)); 455 // IInspectable cornerRes = Application::Current().Resources().Lookup(box_value(L"TextControlCornerRadius")); 456 457 Brush bgBrush = unbox_value<Brush>(bgRes); 458 Thickness border = unbox_value<Thickness>(borderThicknessRes); 459 Brush borderBrush = unbox_value<Brush>(borderBrushRes); 460 CornerRadius cornerRadius = { 4, 4, 4, 4 }; //unbox_value<CornerRadius>(cornerRes); 461 462 pathbar.Background(bgBrush); 463 pathbar.BorderBrush(borderBrush); 464 pathbar.BorderThickness(border); 465 pathbar.CornerRadius(cornerRadius); 466 467 Grid content = Grid(); 468 pathbar.Child(content); 469 470 GridLength gl; 471 gl.Value = 0; 472 gl.GridUnitType = GridUnitType::Auto; 473 474 ColumnDefinition coldef = ColumnDefinition(); 475 coldef.Width(gl); 476 content.ColumnDefinitions().Append(coldef); 477 478 gl.Value = 1; 479 gl.GridUnitType = GridUnitType::Star; 480 481 ColumnDefinition coldef2 = ColumnDefinition(); 482 coldef2.Width(gl); 483 content.ColumnDefinitions().Append(coldef2); 484 485 TextBox pathTextBox = TextBox(); 486 Thickness t = { 0, 0, 0, 0 }; 487 CornerRadius c = { 0 ,0, 0, 0 }; 488 pathTextBox.BorderThickness(t); 489 //pathTextBox.CornerRadius(c); 490 491 492 pathTextBox.HorizontalAlignment(HorizontalAlignment::Stretch); 493 content.SetColumn(pathTextBox, 0); 494 content.SetColumnSpan(pathTextBox, 2); 495 496 content.Children().Append(pathTextBox); 497 498 // stackpanel for buttons 499 StackPanel buttons = StackPanel(); 500 buttons.Orientation(Orientation::Horizontal); 501 buttons.Visibility(Visibility::Collapsed); 502 content.SetColumn(buttons, 0); 503 content.Children().Append(buttons); 504 505 TextBlock filler = TextBlock(); 506 filler.VerticalAlignment(VerticalAlignment::Stretch); 507 //filler.Text(winrt::hstring(L"hello filler")); 508 509 filler.HorizontalAlignment(HorizontalAlignment::Stretch); 510 filler.VerticalAlignment(VerticalAlignment::Stretch); 511 content.SetColumn(filler, 1); 512 content.Children().Append(filler); 513 514 filler.PointerPressed( 515 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( 516 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { 517 pathTextBox.Visibility(Visibility::Visible); 518 buttons.Visibility(Visibility::Collapsed); 519 filler.Visibility(Visibility::Collapsed); 520 pathTextBox.SelectionStart(pathTextBox.Text().size()); 521 pathTextBox.SelectionLength(0); 522 pathTextBox.Focus(FocusState::Keyboard); 523 }) 524 ); 525 526 //pathTextBox.Visibility(Visibility::Collapsed); 527 528 UiPathTextField* uipathbar = new UiPathTextField; 529 ui_context_add_pathtextfield_destructor(current->ctx, uipathbar); 530 uipathbar->grid = content; 531 uipathbar->buttons = buttons; 532 uipathbar->textbox = pathTextBox; 533 uipathbar->filler = filler; 534 uipathbar->obj = obj; 535 uipathbar->getpathelm = args.getpathelm ? args.getpathelm : default_pathelm_func; 536 uipathbar->getpathelmdata = args.getpathelmdata; 537 uipathbar->onactivate = args.onactivate; 538 uipathbar->onactivatedata = args.onactivatedata; 539 uipathbar->ondragstart = args.ondragstart; 540 uipathbar->ondragstartdata = args.ondragstartdata; 541 uipathbar->ondragcomplete = args.ondragcomplete; 542 uipathbar->ondragcompletedata = args.ondragcompletedata; 543 uipathbar->ondrop = args.ondrop; 544 uipathbar->ondropdata = args.ondropsdata; 545 546 547 pathTextBox.KeyDown( 548 winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( 549 [=](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) { 550 auto key = e.Key(); 551 bool showButtons = false; 552 bool update = false; 553 if (key == Windows::System::VirtualKey::Escape) { 554 showButtons = true; 555 } 556 else if (key == Windows::System::VirtualKey::Enter) { 557 showButtons = true; 558 update = true; 559 } 560 561 if (showButtons) { 562 pathTextBox.Visibility(Visibility::Collapsed); 563 buttons.Visibility(Visibility::Visible); 564 filler.Visibility(Visibility::Visible); 565 if (update) { 566 std::wstring value(pathTextBox.Text()); 567 char* full_path = wchar2utf8(value.c_str(), value.length()); 568 569 if (!ui_pathtextfield_update(uipathbar, full_path)) { 570 UiEvent evt; 571 evt.obj = obj; 572 evt.window = obj->window; 573 evt.document = obj->ctx->document; 574 evt.eventdata = full_path; 575 evt.intval = -1; 576 args.onactivate(&evt, args.onactivatedata); 577 } 578 579 free(full_path); 580 } 581 582 //buttons.Focus(FocusState::Keyboard); 583 } 584 }) 585 ); 586 587 588 UIElement elm = pathbar; 589 UiWidget* widget = new UiWidget(elm); 590 widget->data1 = uipathbar; 591 ui_context_add_widget_destructor(current->ctx, widget); 592 593 // bind var 594 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); 595 if (var) { 596 UiString* value = (UiString*)var->value; 597 value->obj = uipathbar; 598 value->get = ui_path_textfield_get; 599 value->set = ui_path_textfield_set; 600 } 601 602 // add listview to current container 603 UI_APPLY_LAYOUT1(current, args); 604 605 current->container->Add(pathbar, false); 606 607 return widget; 608 } 609