tb_editfield.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. // ================================================================================
  2. // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
  3. // == See tb_core.h for more information. ==
  4. // ================================================================================
  5. #include "tb_editfield.h"
  6. #include "tb_select.h"
  7. #include "tb_menu_window.h"
  8. #include "tb_system.h"
  9. #include "tb_language.h"
  10. #include "tb_style_edit_content.h"
  11. #include "tb_widgets_reader.h"
  12. #include "tb_widget_skin_condition_context.h"
  13. #include "tb_font_renderer.h"
  14. #include "tb_skin_util.h"
  15. namespace tb {
  16. const int CARET_BLINK_TIME = 500;
  17. const int SELECTION_SCROLL_DELAY = 1000/30;
  18. /** Get the delta that should be scrolled if dragging the pointer outside the range min-max */
  19. int GetSelectionScrollSpeed(int pointerpos, int min, int max)
  20. {
  21. int d = 0;
  22. if (pointerpos < min)
  23. d = pointerpos - min;
  24. else if (pointerpos > max)
  25. d = pointerpos - max;
  26. d *= d;
  27. d /= 40;
  28. return (pointerpos < min) ? -d : d;
  29. }
  30. TBEditField::TBEditField()
  31. : m_edit_type(EDIT_TYPE_TEXT)
  32. , m_adapt_to_content_size(false)
  33. , m_virtual_width(250)
  34. {
  35. SetIsFocusable(true);
  36. SetWantLongClick(true);
  37. AddChild(&m_scrollbar_x);
  38. AddChild(&m_scrollbar_y);
  39. AddChild(&m_root);
  40. m_root.SetGravity(WIDGET_GRAVITY_ALL);
  41. m_scrollbar_x.SetGravity(WIDGET_GRAVITY_BOTTOM | WIDGET_GRAVITY_LEFT_RIGHT);
  42. m_scrollbar_y.SetGravity(WIDGET_GRAVITY_RIGHT | WIDGET_GRAVITY_TOP_BOTTOM);
  43. m_scrollbar_y.SetAxis(AXIS_Y);
  44. int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
  45. int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
  46. m_scrollbar_x.SetRect(TBRect(0, - scrollbar_x_h, - scrollbar_y_w, scrollbar_x_h));
  47. m_scrollbar_y.SetRect(TBRect(- scrollbar_y_w, 0, scrollbar_y_w, 0));
  48. m_scrollbar_x.SetOpacity(0);
  49. m_scrollbar_y.SetOpacity(0);
  50. SetSkinBg(TBIDC("TBEditField"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
  51. m_style_edit.SetListener(this);
  52. m_root.SetRect(GetVisibleRect());
  53. m_placeholder.SetTextAlign(TB_TEXT_ALIGN_LEFT);
  54. m_content_factory.editfield = this;
  55. m_style_edit.SetContentFactory(&m_content_factory);
  56. }
  57. TBEditField::~TBEditField()
  58. {
  59. RemoveChild(&m_root);
  60. RemoveChild(&m_scrollbar_y);
  61. RemoveChild(&m_scrollbar_x);
  62. }
  63. TBRect TBEditField::GetVisibleRect()
  64. {
  65. TBRect rect = GetPaddingRect();
  66. if (m_scrollbar_y.GetOpacity())
  67. rect.w -= m_scrollbar_y.GetRect().w;
  68. if (m_scrollbar_x.GetOpacity())
  69. rect.h -= m_scrollbar_x.GetRect().h;
  70. return rect;
  71. }
  72. void TBEditField::UpdateScrollbarVisibility(bool multiline)
  73. {
  74. bool enable_vertical = multiline && !m_adapt_to_content_size;
  75. m_scrollbar_y.SetOpacity(enable_vertical ? 1.f : 0.f);
  76. m_root.SetRect(GetVisibleRect());
  77. }
  78. void TBEditField::SetAdaptToContentSize(bool adapt)
  79. {
  80. if (m_adapt_to_content_size == adapt)
  81. return;
  82. m_adapt_to_content_size = adapt;
  83. UpdateScrollbarVisibility(GetMultiline());
  84. }
  85. void TBEditField::SetVirtualWidth(int virtual_width)
  86. {
  87. if (m_virtual_width == virtual_width)
  88. return;
  89. m_virtual_width = virtual_width;
  90. if (m_adapt_to_content_size && m_style_edit.packed.wrapping)
  91. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  92. }
  93. void TBEditField::SetMultiline(bool multiline)
  94. {
  95. if (multiline == GetMultiline())
  96. return;
  97. UpdateScrollbarVisibility(multiline);
  98. m_style_edit.SetMultiline(multiline);
  99. SetWrapping(multiline);
  100. InvalidateSkinStates();
  101. TBWidget::Invalidate();
  102. }
  103. void TBEditField::SetStyling(bool styling)
  104. {
  105. m_style_edit.SetStyling(styling);
  106. }
  107. void TBEditField::SetReadOnly(bool readonly)
  108. {
  109. if (readonly == GetReadOnly())
  110. return;
  111. m_style_edit.SetReadOnly(readonly);
  112. InvalidateSkinStates();
  113. TBWidget::Invalidate();
  114. }
  115. void TBEditField::SetWrapping(bool wrapping)
  116. {
  117. if (wrapping == GetWrapping())
  118. return;
  119. m_style_edit.SetWrapping(wrapping);
  120. // Invalidate the layout when the wrap mode change and we should adapt our size to it
  121. if (m_adapt_to_content_size)
  122. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  123. }
  124. void TBEditField::SetEditType(EDIT_TYPE type)
  125. {
  126. if (m_edit_type == type)
  127. return;
  128. m_edit_type = type;
  129. m_style_edit.SetPassword(type == EDIT_TYPE_PASSWORD);
  130. InvalidateSkinStates();
  131. TBWidget::Invalidate();
  132. }
  133. bool TBEditField::GetCustomSkinCondition(const TBSkinCondition::CONDITION_INFO &info)
  134. {
  135. if (info.custom_prop == TBIDC("edit-type"))
  136. {
  137. switch (m_edit_type)
  138. {
  139. case EDIT_TYPE_TEXT: return info.value == TBIDC("text");
  140. case EDIT_TYPE_SEARCH: return info.value == TBIDC("search");
  141. case EDIT_TYPE_PASSWORD: return info.value == TBIDC("password");
  142. case EDIT_TYPE_EMAIL: return info.value == TBIDC("email");
  143. case EDIT_TYPE_PHONE: return info.value == TBIDC("phone");
  144. case EDIT_TYPE_URL: return info.value == TBIDC("url");
  145. case EDIT_TYPE_NUMBER: return info.value == TBIDC("number");
  146. };
  147. }
  148. else if (info.custom_prop == TBIDC("multiline"))
  149. return !((uint32)info.value) == !GetMultiline();
  150. else if (info.custom_prop == TBIDC("readonly"))
  151. return !((uint32)info.value) == !GetReadOnly();
  152. return false;
  153. }
  154. void TBEditField::ScrollTo(int x, int y)
  155. {
  156. int old_x = m_scrollbar_x.GetValue();
  157. int old_y = m_scrollbar_y.GetValue();
  158. m_style_edit.SetScrollPos(x, y);
  159. if (old_x != m_scrollbar_x.GetValue() ||
  160. old_y != m_scrollbar_y.GetValue())
  161. TBWidget::Invalidate();
  162. }
  163. TBWidget::ScrollInfo TBEditField::GetScrollInfo()
  164. {
  165. ScrollInfo info;
  166. info.min_x = static_cast<int>(m_scrollbar_x.GetMinValue());
  167. info.min_y = static_cast<int>(m_scrollbar_y.GetMinValue());
  168. info.max_x = static_cast<int>(m_scrollbar_x.GetMaxValue());
  169. info.max_y = static_cast<int>(m_scrollbar_y.GetMaxValue());
  170. info.x = m_scrollbar_x.GetValue();
  171. info.y = m_scrollbar_y.GetValue();
  172. return info;
  173. }
  174. bool TBEditField::OnEvent(const TBWidgetEvent &ev)
  175. {
  176. if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_scrollbar_x)
  177. {
  178. m_style_edit.SetScrollPos(m_scrollbar_x.GetValue(), m_style_edit.scroll_y);
  179. OnScroll(m_scrollbar_x.GetValue(), m_style_edit.scroll_y);
  180. TBWidget::OnEvent(ev);
  181. return true;
  182. }
  183. else if (ev.type == EVENT_TYPE_CHANGED && ev.target == &m_scrollbar_y)
  184. {
  185. m_style_edit.SetScrollPos(m_style_edit.scroll_x, m_scrollbar_y.GetValue());
  186. OnScroll(m_style_edit.scroll_x, m_scrollbar_y.GetValue());
  187. TBWidget::OnEvent(ev);
  188. return true;
  189. }
  190. else if (ev.type == EVENT_TYPE_CHANGED)
  191. {
  192. TBWidget::OnEvent(ev);
  193. }
  194. else if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE)
  195. {
  196. int old_val = m_scrollbar_y.GetValue();
  197. m_scrollbar_y.SetValue(old_val + ev.delta_y * TBSystem::GetPixelsPerLine());
  198. return m_scrollbar_y.GetValue() != old_val;
  199. }
  200. else if (ev.type == EVENT_TYPE_POINTER_DOWN && ev.target == this)
  201. {
  202. TBRect padding_rect = GetPaddingRect();
  203. if (m_style_edit.MouseDown(
  204. TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y),
  205. 1, ev.count, ev.modifierkeys, ev.touch))
  206. {
  207. // Post a message to start selection scroll
  208. PostMessageDelayed(TBIDC("selscroll"), nullptr, SELECTION_SCROLL_DELAY);
  209. // forward to delegate, if any
  210. TBWidget::OnEvent(ev);
  211. return true;
  212. }
  213. }
  214. else if (ev.type == EVENT_TYPE_POINTER_MOVE && ev.target == this)
  215. {
  216. TBRect padding_rect = GetPaddingRect();
  217. return m_style_edit.MouseMove(TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y));
  218. }
  219. else if (ev.type == EVENT_TYPE_POINTER_UP && ev.target == this)
  220. {
  221. TBRect padding_rect = GetPaddingRect();
  222. return m_style_edit.MouseUp(TBPoint(ev.target_x - padding_rect.x, ev.target_y - padding_rect.y),
  223. 1, ev.modifierkeys, ev.touch);
  224. }
  225. else if (ev.type == EVENT_TYPE_KEY_DOWN)
  226. {
  227. TBWidget::OnEvent(ev);
  228. if (ev.special_key == TB_KEY_ENTER || ev.special_key == TB_KEY_ESC)
  229. if (!m_style_edit.packed.read_only && !m_style_edit.packed.multiline_on)
  230. {
  231. if (focused_widget == this)
  232. {
  233. if (ev.special_key == TB_KEY_ESC)
  234. {
  235. SetText(m_initial_edit_text);
  236. }
  237. TBWidgetListener::InvokeWidgetFocusChanged(focused_widget, false);
  238. focused_widget->OnFocusChanged(false);
  239. focused_widget = nullptr;
  240. return true;
  241. }
  242. }
  243. return m_style_edit.KeyDown(ev.key, ev.special_key, ev.modifierkeys);
  244. }
  245. else if (ev.type == EVENT_TYPE_KEY_UP)
  246. {
  247. TBWidget::OnEvent(ev);
  248. return true;
  249. }
  250. else if ((ev.type == EVENT_TYPE_CLICK && ev.target->GetID() == TBIDC("popupmenu")) ||
  251. (ev.type == EVENT_TYPE_SHORTCUT))
  252. {
  253. if (ev.ref_id == TBIDC("cut") && !m_style_edit.packed.read_only)
  254. m_style_edit.Cut();
  255. else if (ev.ref_id == TBIDC("copy"))
  256. m_style_edit.Copy();
  257. else if (ev.ref_id == TBIDC("paste") && !m_style_edit.packed.read_only)
  258. m_style_edit.Paste();
  259. else if (ev.ref_id == TBIDC("delete") && !m_style_edit.packed.read_only)
  260. m_style_edit.Delete();
  261. else if (ev.ref_id == TBIDC("undo") && !m_style_edit.packed.read_only)
  262. m_style_edit.Undo();
  263. else if (ev.ref_id == TBIDC("redo") && !m_style_edit.packed.read_only)
  264. m_style_edit.Redo();
  265. else if (ev.ref_id == TBIDC("selectall"))
  266. m_style_edit.selection.SelectAll();
  267. else
  268. return false;
  269. return true;
  270. }
  271. else if ((ev.type == EVENT_TYPE_CONTEXT_MENU || ev.type == EVENT_TYPE_RIGHT_POINTER_UP) && ev.target == this)
  272. {
  273. TBPoint pos_in_root(ev.target_x, ev.target_y);
  274. //ev.target->ConvertToRoot(pos_in_root.x, pos_in_root.y);
  275. if (TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC("popupmenu")))
  276. {
  277. TBGenericStringItemSource *source = menu->GetList()->GetDefaultSource();
  278. source->AddItem(new TBGenericStringItem(g_tb_lng->GetString(TBIDC("cut")), TBIDC("cut")));
  279. source->AddItem(new TBGenericStringItem(g_tb_lng->GetString(TBIDC("copy")), TBIDC("copy")));
  280. source->AddItem(new TBGenericStringItem(g_tb_lng->GetString(TBIDC("paste")), TBIDC("paste")));
  281. source->AddItem(new TBGenericStringItem(g_tb_lng->GetString(TBIDC("delete")), TBIDC("delete")));
  282. source->AddItem(new TBGenericStringItem("-"));
  283. source->AddItem(new TBGenericStringItem(g_tb_lng->GetString(TBIDC("selectall")), TBIDC("selectall")));
  284. menu->Show(source, TBPopupAlignment(pos_in_root), -1);
  285. }
  286. return true;
  287. }
  288. return false;
  289. }
  290. void TBEditField::OnPaint(const PaintProps &paint_props)
  291. {
  292. TBRect visible_rect = GetVisibleRect();
  293. bool clip = m_scrollbar_x.CanScroll() || m_scrollbar_y.CanScroll();
  294. TBRect old_clip;
  295. if (clip)
  296. old_clip = g_renderer->SetClipRect(visible_rect, true);
  297. int trans_x = visible_rect.x, trans_y = visible_rect.y;
  298. g_renderer->Translate(trans_x, trans_y);
  299. // Draw text content, caret etc.
  300. visible_rect.x = visible_rect.y = 0;
  301. m_style_edit.Paint(visible_rect, GetCalculatedFontDescription(), paint_props.text_color);
  302. // If empty, draw placeholder text with some opacity.
  303. if (m_style_edit.IsEmpty())
  304. {
  305. float old_opacity = g_renderer->GetOpacity();
  306. g_renderer->SetOpacity(old_opacity * g_tb_skin->GetDefaultPlaceholderOpacity());
  307. TBRect placeholder_rect(visible_rect.x, visible_rect.y, visible_rect.w, GetFont()->GetHeight());
  308. m_placeholder.Paint(this, placeholder_rect, paint_props.text_color);
  309. g_renderer->SetOpacity(old_opacity);
  310. }
  311. g_renderer->Translate(-trans_x, -trans_y);
  312. if (clip)
  313. g_renderer->SetClipRect(old_clip, false);
  314. }
  315. void TBEditField::OnPaintChildren(const PaintProps &paint_props)
  316. {
  317. TBWidget::OnPaintChildren(paint_props);
  318. // Draw fadeout skin at the needed edges.
  319. DrawEdgeFadeout(GetVisibleRect(),
  320. TBIDC("TBEditField.fadeout_x"),
  321. TBIDC("TBEditField.fadeout_y"),
  322. m_scrollbar_x.GetValue(),
  323. m_scrollbar_y.GetValue(),
  324. (int)(m_scrollbar_x.GetMaxValue() - m_scrollbar_x.GetValueDouble()),
  325. (int)(m_scrollbar_y.GetMaxValue() - m_scrollbar_y.GetValueDouble()));
  326. }
  327. void TBEditField::OnAdded()
  328. {
  329. m_style_edit.SetFont(GetCalculatedFontDescription());
  330. }
  331. void TBEditField::OnFontChanged()
  332. {
  333. m_style_edit.SetFont(GetCalculatedFontDescription());
  334. }
  335. void TBEditField::OnFocusChanged(bool focused)
  336. {
  337. m_style_edit.Focus(focused);
  338. if (focused)
  339. {
  340. if (!m_style_edit.packed.multiline_on)
  341. {
  342. m_initial_edit_text.Clear();
  343. GetText(m_initial_edit_text);
  344. }
  345. }
  346. else
  347. {
  348. if (!m_style_edit.packed.multiline_on)
  349. {
  350. TBStr curText;
  351. GetText(curText);
  352. if (!curText.Equals(m_initial_edit_text))
  353. {
  354. TBWidgetEvent ev(EVENT_TYPE_CUSTOM);
  355. // TBIDC does not register the TBID with the UI system, so do it this way
  356. TBID refid("edit_complete");
  357. ev.ref_id = refid;
  358. // forward to delegate
  359. TBWidget::OnEvent(ev);
  360. }
  361. }
  362. }
  363. TBWidget::OnFocusChanged(focused);
  364. }
  365. void TBEditField::OnResized(int old_w, int old_h)
  366. {
  367. // Make the scrollbars move
  368. TBWidget::OnResized(old_w, old_h);
  369. TBRect visible_rect = GetVisibleRect();
  370. m_style_edit.SetLayoutSize(visible_rect.w, visible_rect.h, false);
  371. UpdateScrollbars();
  372. }
  373. PreferredSize TBEditField::OnCalculatePreferredContentSize(const SizeConstraints &constraints)
  374. {
  375. // ATOMIC BEGIN
  376. // TurboBadger uses font height here, we add 2 pixels to calculation
  377. // as this gives better default breathing room and avoids pixels being cut off
  378. int font_height = GetFont()->GetHeight() + 2;
  379. // ATOMIC END
  380. PreferredSize ps;
  381. if (m_adapt_to_content_size)
  382. {
  383. int old_layout_width = m_style_edit.layout_width;
  384. int old_layout_height = m_style_edit.layout_height;
  385. if (m_style_edit.packed.wrapping)
  386. {
  387. // If we have wrapping enabled, we have to set a virtual width and format the text
  388. // so we can get the actual content width with a constant result every time.
  389. // If the layouter does not respect our size constraints in the end, we may
  390. // get a completly different content height due to different wrapping.
  391. // To fix that, we need to layout in 2 passes.
  392. // A hacky fix is to do something we probably shouldn't: use the old layout width
  393. // as virtual width for the new.
  394. //int layout_width = old_layout_width > 0 ? MAX(old_layout_width, m_virtual_width) : m_virtual_width;
  395. int layout_width = m_virtual_width;
  396. if (constraints.available_w != SizeConstraints::NO_RESTRICTION)
  397. {
  398. layout_width = constraints.available_w;
  399. if (TBSkinElement *bg_skin = GetSkinBgElement())
  400. layout_width -= bg_skin->padding_left + bg_skin->padding_right;
  401. }
  402. m_style_edit.SetLayoutSize(layout_width, old_layout_height, true);
  403. ps.size_dependency = SIZE_DEP_HEIGHT_DEPEND_ON_WIDTH;
  404. }
  405. int width = m_style_edit.GetContentWidth();
  406. int height = m_style_edit.GetContentHeight();
  407. if (m_style_edit.packed.wrapping)
  408. m_style_edit.SetLayoutSize(old_layout_width, old_layout_height, true);
  409. height = MAX(height, font_height);
  410. ps.min_w = ps.pref_w /*= ps.max_w*/ = width; // should go with the hack above.
  411. //ps.min_w = ps.pref_w = ps.max_w = width;
  412. ps.min_h = ps.pref_h = ps.max_h = height;
  413. }
  414. else
  415. {
  416. ps.pref_h = ps.min_h = font_height;
  417. if (m_style_edit.packed.multiline_on)
  418. {
  419. ps.pref_w = font_height * 10;
  420. ps.pref_h = font_height * 5;
  421. }
  422. else
  423. ps.max_h = ps.pref_h;
  424. }
  425. return ps;
  426. }
  427. void TBEditField::OnMessageReceived(TBMessage *msg)
  428. {
  429. if (msg->message == TBIDC("blink"))
  430. {
  431. m_style_edit.caret.on = !m_style_edit.caret.on;
  432. m_style_edit.caret.Invalidate();
  433. // Post another blink message so we blink again.
  434. PostMessageDelayed(TBIDC("blink"), nullptr, CARET_BLINK_TIME);
  435. }
  436. else if (msg->message == TBIDC("selscroll") && captured_widget == this)
  437. {
  438. // Get scroll speed from where mouse is relative to the padding rect.
  439. TBRect padding_rect = GetVisibleRect().Shrink(2, 2);
  440. int dx = GetSelectionScrollSpeed(pointer_move_widget_x, padding_rect.x, padding_rect.x + padding_rect.w);
  441. int dy = GetSelectionScrollSpeed(pointer_move_widget_y, padding_rect.y, padding_rect.y + padding_rect.h);
  442. m_scrollbar_x.SetValue(m_scrollbar_x.GetValue() + dx);
  443. m_scrollbar_y.SetValue(m_scrollbar_y.GetValue() + dy);
  444. // Handle mouse move at the new scroll position, so selection is updated
  445. if (dx || dy)
  446. m_style_edit.MouseMove(TBPoint(pointer_move_widget_x, pointer_move_widget_y));
  447. // Post another setscroll message so we continue scrolling if we still should.
  448. if (m_style_edit.select_state)
  449. PostMessageDelayed(TBIDC("selscroll"), nullptr, SELECTION_SCROLL_DELAY);
  450. }
  451. }
  452. void TBEditField::OnChange()
  453. {
  454. // Invalidate the layout when the content change and we should adapt our size to it
  455. if (m_adapt_to_content_size)
  456. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  457. TBWidgetEvent ev(EVENT_TYPE_CHANGED);
  458. InvokeEvent(ev);
  459. }
  460. bool TBEditField::OnEnter()
  461. {
  462. return false;
  463. }
  464. void TBEditField::Invalidate(const TBRect &rect)
  465. {
  466. TBWidget::Invalidate();
  467. }
  468. void TBEditField::DrawString(int32 x, int32 y, TBFontFace *font, const TBColor &color, const char *str, int32 len)
  469. {
  470. font->DrawString(x, y, color, str, len);
  471. }
  472. void TBEditField::DrawRect(const TBRect &rect, const TBColor &color)
  473. {
  474. g_renderer->DrawRect(rect, color);
  475. }
  476. void TBEditField::DrawRectFill(const TBRect &rect, const TBColor &color)
  477. {
  478. g_renderer->DrawRectFill(rect, color);
  479. }
  480. void TBEditField::DrawTextSelectionBg(const TBRect &rect)
  481. {
  482. TBWidgetSkinConditionContext context(this);
  483. g_tb_skin->PaintSkin(rect, TBIDC("TBEditField.selection"), static_cast<SKIN_STATE>(GetAutoState()), context);
  484. }
  485. void TBEditField::DrawContentSelectionFg(const TBRect &rect)
  486. {
  487. TBWidgetSkinConditionContext context(this);
  488. g_tb_skin->PaintSkin(rect, TBIDC("TBEditField.selection"), static_cast<SKIN_STATE>(GetAutoState()), context);
  489. }
  490. void TBEditField::DrawCaret(const TBRect &rect)
  491. {
  492. if (GetIsFocused() && !m_style_edit.packed.read_only)
  493. DrawTextSelectionBg(rect);
  494. }
  495. void TBEditField::Scroll(int32 dx, int32 dy)
  496. {
  497. TBWidget::Invalidate();
  498. m_scrollbar_x.SetValue(m_style_edit.scroll_x);
  499. m_scrollbar_y.SetValue(m_style_edit.scroll_y);
  500. }
  501. void TBEditField::UpdateScrollbars()
  502. {
  503. int32 w = m_style_edit.layout_width;
  504. int32 h = m_style_edit.layout_height;
  505. m_scrollbar_x.SetLimits(0, m_style_edit.GetContentWidth() - w, w);
  506. m_scrollbar_y.SetLimits(0, m_style_edit.GetContentHeight() - h, h);
  507. }
  508. void TBEditField::CaretBlinkStart()
  509. {
  510. // Post the delayed blink message if we don't already have one
  511. if (!GetMessageByID(TBIDC("blink")))
  512. PostMessageDelayed(TBIDC("blink"), nullptr, CARET_BLINK_TIME);
  513. }
  514. void TBEditField::CaretBlinkStop()
  515. {
  516. // Remove the blink message if we have one
  517. if (TBMessage *msg = GetMessageByID(TBIDC("blink")))
  518. DeleteMessage(msg);
  519. }
  520. // == TBEditFieldScrollRoot =======================================================================
  521. void TBEditFieldScrollRoot::OnPaintChildren(const PaintProps &paint_props)
  522. {
  523. // Avoid setting clipping (can be expensive) if we have no children to paint anyway.
  524. if (!GetFirstChild())
  525. return;
  526. // Clip children
  527. TBRect old_clip_rect = g_renderer->SetClipRect(GetPaddingRect(), true);
  528. TBWidget::OnPaintChildren(paint_props);
  529. g_renderer->SetClipRect(old_clip_rect, false);
  530. }
  531. void TBEditFieldScrollRoot::GetChildTranslation(int &x, int &y) const
  532. {
  533. TBEditField *edit_field = static_cast<TBEditField *>(GetParent());
  534. x = (int) -edit_field->GetStyleEdit()->scroll_x;
  535. y = (int) -edit_field->GetStyleEdit()->scroll_y;
  536. }
  537. WIDGET_HIT_STATUS TBEditFieldScrollRoot::GetHitStatus(int x, int y)
  538. {
  539. // Return no hit on this widget, but maybe on any of the children.
  540. if (TBWidget::GetHitStatus(x, y) && GetWidgetAt(x, y, false))
  541. return WIDGET_HIT_STATUS_HIT;
  542. return WIDGET_HIT_STATUS_NO_HIT;
  543. }
  544. // == TBTextFragmentContentWidget =================================================================
  545. class TBTextFragmentContentWidget : public TBTextFragmentContent
  546. {
  547. public:
  548. TBTextFragmentContentWidget(TBWidget *parent, TBWidget *widget);
  549. virtual ~TBTextFragmentContentWidget();
  550. virtual void UpdatePos(int x, int y);
  551. virtual int32 GetWidth(TBFontFace *font, TBTextFragment *fragment);
  552. virtual int32 GetHeight(TBFontFace *font, TBTextFragment *fragment);
  553. virtual int32 GetBaseline(TBFontFace *font, TBTextFragment *fragment);
  554. private:
  555. TBWidget *m_widget;
  556. };
  557. TBTextFragmentContentWidget::TBTextFragmentContentWidget(TBWidget *parent, TBWidget *widget)
  558. : m_widget(widget)
  559. {
  560. parent->GetContentRoot()->AddChild(widget);
  561. }
  562. TBTextFragmentContentWidget::~TBTextFragmentContentWidget()
  563. {
  564. m_widget->GetParent()->RemoveChild(m_widget);
  565. delete m_widget;
  566. }
  567. void TBTextFragmentContentWidget::UpdatePos(int x, int y)
  568. {
  569. m_widget->SetRect(TBRect(x, y, GetWidth(nullptr, nullptr), GetHeight(nullptr, nullptr)));
  570. }
  571. int32 TBTextFragmentContentWidget::GetWidth(TBFontFace *font, TBTextFragment *fragment)
  572. {
  573. return m_widget->GetRect().w ? m_widget->GetRect().w : m_widget->GetPreferredSize().pref_w;
  574. }
  575. int32 TBTextFragmentContentWidget::GetHeight(TBFontFace *font, TBTextFragment *fragment)
  576. {
  577. return m_widget->GetRect().h ? m_widget->GetRect().h : m_widget->GetPreferredSize().pref_h;
  578. }
  579. int32 TBTextFragmentContentWidget::GetBaseline(TBFontFace *font, TBTextFragment *fragment)
  580. {
  581. int height = GetHeight(font, fragment);
  582. return (height + fragment->block->CalculateBaseline(font)) / 2;
  583. }
  584. // == TBEditFieldContentFactory ===================================================================
  585. int TBEditFieldContentFactory::GetContent(const char *text)
  586. {
  587. return TBTextFragmentContentFactory::GetContent(text);
  588. }
  589. TBTextFragmentContent *TBEditFieldContentFactory::CreateFragmentContent(const char *text, int text_len)
  590. {
  591. // ATOMIC BEGIN
  592. // https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1297
  593. // ATOMIC END
  594. if (strncmp(text, "<widget ", MIN(text_len, 8)) == 0)
  595. {
  596. // Create a wrapper for the generated widget.
  597. // Its size will adapt to the content.
  598. if (TBWidget *widget = new TBWidget())
  599. {
  600. if (TBTextFragmentContentWidget *cw = new TBTextFragmentContentWidget(editfield, widget))
  601. {
  602. g_widgets_reader->LoadData(widget, text + 8, text_len - 9);
  603. return cw;
  604. }
  605. delete widget;
  606. }
  607. }
  608. return TBTextFragmentContentFactory::CreateFragmentContent(text, text_len);
  609. }
  610. }; // namespace tb