Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1392 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <stdarg.h>
  9. #include <stdio.h>
  10. #include <ctype.h>
  11. #include <vgui/IInput.h>
  12. #include <vgui/ILocalize.h>
  13. #include <vgui/IPanel.h>
  14. #include <vgui/ISurface.h>
  15. #include <vgui/IScheme.h>
  16. #include <KeyValues.h>
  17. #include <vgui_controls/Label.h>
  18. #include <vgui_controls/Image.h>
  19. #include <vgui_controls/TextImage.h>
  20. #include <vgui_controls/Controls.h>
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include <tier0/memdbgon.h>
  23. using namespace vgui;
  24. #ifndef max
  25. #define max(a,b) (((a) > (b)) ? (a) : (b))
  26. #endif
  27. DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Label, Label );
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Constructor
  30. //-----------------------------------------------------------------------------
  31. Label::Label(Panel *parent, const char *panelName, const char *text) : BaseClass(parent, panelName)
  32. {
  33. Init();
  34. _textImage = new TextImage(text);
  35. _textImage->SetColor(Color(0, 0, 0, 0));
  36. SetText(text);
  37. _textImageIndex = AddImage(_textImage, 0);
  38. REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" );
  39. }
  40. //-----------------------------------------------------------------------------
  41. // Purpose: Constructor
  42. //-----------------------------------------------------------------------------
  43. Label::Label(Panel *parent, const char *panelName, const wchar_t *wszText) : BaseClass(parent, panelName)
  44. {
  45. Init();
  46. _textImage = new TextImage(wszText);
  47. _textImage->SetColor(Color(0, 0, 0, 0));
  48. SetText(wszText);
  49. _textImageIndex = AddImage(_textImage, 0);
  50. REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" );
  51. }
  52. //-----------------------------------------------------------------------------
  53. // Purpose: Destructor
  54. //-----------------------------------------------------------------------------
  55. Label::~Label()
  56. {
  57. delete _textImage;
  58. delete [] _associateName;
  59. delete [] _fontOverrideName;
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Construct the label
  63. //-----------------------------------------------------------------------------
  64. void Label::Init()
  65. {
  66. _contentAlignment = a_west;
  67. _textColorState = CS_NORMAL;
  68. _textInset[0] = 0;
  69. _textInset[1] = 0;
  70. _hotkey = 0;
  71. _associate = NULL;
  72. _associateName = NULL;
  73. _fontOverrideName = NULL;
  74. m_bWrap = false;
  75. m_bCenterWrap = false;
  76. m_bAutoWideToContents = false;
  77. m_bUseProportionalInsets = false;
  78. m_bAutoWideDirty = false;
  79. // SetPaintBackgroundEnabled(false);
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Set whether the text is displayed bright or dull
  83. //-----------------------------------------------------------------------------
  84. void Label::SetTextColorState(EColorState state)
  85. {
  86. if (_textColorState != state)
  87. {
  88. _textColorState = state;
  89. InvalidateLayout();
  90. }
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Return the full size of the contained content
  94. //-----------------------------------------------------------------------------
  95. void Label::GetContentSize(int &wide, int &tall)
  96. {
  97. if( GetFont() == INVALID_FONT ) // we haven't loaded our font yet, so load it now
  98. {
  99. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  100. if ( pScheme )
  101. {
  102. SetFont( pScheme->GetFont( "Default", IsProportional() ) );
  103. }
  104. }
  105. int tx0, ty0, tx1, ty1;
  106. ComputeAlignment(tx0, ty0, tx1, ty1);
  107. // the +8 is padding to the content size
  108. // the code which uses it should really set that itself;
  109. // however a lot of existing code relies on this
  110. wide = (tx1 - tx0) + _textInset[0];
  111. // get the size of the text image and remove it
  112. int iWide, iTall;
  113. _textImage->GetSize(iWide, iTall);
  114. wide -= iWide;
  115. // get the full, untruncated (no elipsis) size of the text image.
  116. _textImage->GetContentSize(iWide, iTall);
  117. wide += iWide;
  118. // addin the image offsets as well
  119. for (int i=0; i < _imageDar.Size(); i++)
  120. wide += _imageDar[i].offset;
  121. tall = max((ty1 - ty0) + _textInset[1], iTall);
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose: Calculate the keyboard key that is a hotkey
  125. //-----------------------------------------------------------------------------
  126. wchar_t Label::CalculateHotkey(const char *text)
  127. {
  128. for (const char *ch = text; *ch != 0; ch++)
  129. {
  130. if (*ch == '&')
  131. {
  132. // get the next character
  133. ch++;
  134. if (*ch == '&')
  135. {
  136. // just an &
  137. continue;
  138. }
  139. else if (*ch == 0)
  140. {
  141. break;
  142. }
  143. else if (V_isalnum(*ch))
  144. {
  145. // found the hotkey
  146. return (wchar_t)tolower(*ch);
  147. }
  148. }
  149. }
  150. return '\0';
  151. }
  152. wchar_t Label::CalculateHotkey(const wchar_t *text)
  153. {
  154. if( text )
  155. {
  156. for (const wchar_t *ch = text; *ch != 0; ch++)
  157. {
  158. if (*ch == '&')
  159. {
  160. // get the next character
  161. ch++;
  162. if (*ch == '&')
  163. {
  164. // just an &
  165. continue;
  166. }
  167. else if (*ch == 0)
  168. {
  169. break;
  170. }
  171. else if (iswalnum(*ch))
  172. {
  173. // found the hotkey
  174. return (wchar_t)towlower(*ch);
  175. }
  176. }
  177. }
  178. }
  179. return '\0';
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose: Check if this label has a hotkey that is the key passed in.
  183. //-----------------------------------------------------------------------------
  184. Panel *Label::HasHotkey(wchar_t key)
  185. {
  186. #ifdef VGUI_HOTKEYS_ENABLED
  187. if ( iswalnum( key ) )
  188. key = towlower( key );
  189. if (_hotkey == key)
  190. return this;
  191. #endif
  192. return NULL;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: Set the hotkey for this label
  196. //-----------------------------------------------------------------------------
  197. void Label::SetHotkey(wchar_t ch)
  198. {
  199. _hotkey = ch;
  200. }
  201. wchar_t Label::GetHotKey()
  202. {
  203. return _hotkey;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose: Handle a hotkey by passing on focus to associate
  207. //-----------------------------------------------------------------------------
  208. void Label::OnHotkeyPressed()
  209. {
  210. // we can't accept focus, but if we are associated to a control give it to that
  211. if (_associate.Get() && !IsBuildModeActive())
  212. {
  213. _associate->RequestFocus();
  214. }
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose: Redirect mouse pressed to giving focus to associate
  218. //-----------------------------------------------------------------------------
  219. void Label::OnMousePressed(MouseCode code)
  220. {
  221. if (_associate.Get() && !IsBuildModeActive())
  222. {
  223. _associate->RequestFocus();
  224. }
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Return the text in the label
  228. //-----------------------------------------------------------------------------
  229. void Label::GetText(char *textOut, int bufferLen)
  230. {
  231. _textImage->GetText(textOut, bufferLen);
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Purpose: Return the text in the label
  235. //-----------------------------------------------------------------------------
  236. void Label::GetText(wchar_t *textOut, int bufLenInBytes)
  237. {
  238. _textImage->GetText(textOut, bufLenInBytes);
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Take the string and looks it up in the localization file
  242. // to convert it to unicode
  243. // Setting the text will not set the size of the label.
  244. // Set the size explicitly or use setToContent()
  245. //-----------------------------------------------------------------------------
  246. void Label::SetText(const char *text)
  247. {
  248. // if set to null, just make blank
  249. if (!text)
  250. {
  251. text = "";
  252. }
  253. // let the text image do the translation itself
  254. _textImage->SetText(text);
  255. if( text[0] == '#' )
  256. {
  257. SetHotkey(CalculateHotkey(g_pVGuiLocalize->Find(text)));
  258. }
  259. else
  260. {
  261. SetHotkey(CalculateHotkey(text));
  262. }
  263. m_bAutoWideDirty = m_bAutoWideToContents;
  264. InvalidateLayout();
  265. Repaint();
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose: Set unicode text directly
  269. //-----------------------------------------------------------------------------
  270. void Label::SetText(const wchar_t *unicodeString, bool bClearUnlocalizedSymbol)
  271. {
  272. m_bAutoWideDirty = m_bAutoWideToContents;
  273. if ( unicodeString && _textImage->GetUText() && !Q_wcscmp(unicodeString,_textImage->GetUText()) )
  274. return;
  275. _textImage->SetText(unicodeString, bClearUnlocalizedSymbol);
  276. //!! need to calculate hotkey from translated string
  277. SetHotkey(CalculateHotkey(unicodeString));
  278. InvalidateLayout(); // possible that the textimage needs to expand
  279. Repaint();
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose: updates localized text
  283. //-----------------------------------------------------------------------------
  284. void Label::OnDialogVariablesChanged(KeyValues *dialogVariables )
  285. {
  286. StringIndex_t index = _textImage->GetUnlocalizedTextSymbol();
  287. if (index != INVALID_LOCALIZE_STRING_INDEX)
  288. {
  289. // reconstruct the string from the variables
  290. wchar_t buf[1024];
  291. g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables);
  292. SetText(buf);
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Additional offset at the Start of the text (from whichever side it is aligned)
  297. //-----------------------------------------------------------------------------
  298. void Label::SetTextInset(int xInset, int yInset)
  299. {
  300. _textInset[0] = xInset;
  301. _textInset[1] = yInset;
  302. int wide, tall;
  303. GetSize( wide, tall);
  304. _textImage->SetDrawWidth(wide - _textInset[0]);
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. //-----------------------------------------------------------------------------
  309. void Label::GetTextInset(int *xInset, int *yInset )
  310. {
  311. if ( xInset )
  312. {
  313. *xInset = _textInset[0];
  314. }
  315. if ( yInset )
  316. {
  317. *yInset = _textInset[1];
  318. }
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Set the enabled state
  322. //-----------------------------------------------------------------------------
  323. void Label::SetEnabled(bool state)
  324. {
  325. Panel::SetEnabled(state);
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose: Calculates where in the panel the content resides
  329. // Input : &tx0 - [out] position of the content
  330. // &ty0 -
  331. // &tx1 -
  332. // &ty1 -
  333. // Note: horizontal alignment is west if the image dar has
  334. // more than one image in it, this is because we use image sizes
  335. // to determine layout in classes for example, Menu.
  336. //-----------------------------------------------------------------------------
  337. void Label::ComputeAlignment(int &tx0, int &ty0, int &tx1, int &ty1)
  338. {
  339. int wide, tall;
  340. GetPaintSize(wide, tall);
  341. int tWide,tTall;
  342. // text bounding box
  343. tx0 = 0;
  344. ty0 = 0;
  345. // loop through all the images and calculate the complete bounds
  346. int maxX = 0, maxY = 0;
  347. int actualXAlignment = _contentAlignment;
  348. for (int i = 0; i < _imageDar.Count(); i++)
  349. {
  350. TImageInfo &imageInfo = _imageDar[i];
  351. IImage *image = imageInfo.image;
  352. if (!image)
  353. continue; // skip over null images
  354. // add up the bounds
  355. int iWide, iTall;
  356. image->GetSize(iWide, iTall);
  357. if (iWide > wide) // if the image is larger than the label just do a west alignment
  358. actualXAlignment = Label::a_west;
  359. // get the max height
  360. maxY = max(maxY, iTall);
  361. maxX += iWide;
  362. // add the offset to x
  363. maxX += imageInfo.offset;
  364. }
  365. tWide = maxX;
  366. tTall = maxY;
  367. // x align text
  368. switch (actualXAlignment)
  369. {
  370. // left
  371. case Label::a_northwest:
  372. case Label::a_west:
  373. case Label::a_southwest:
  374. {
  375. tx0 = 0;
  376. break;
  377. }
  378. // center
  379. case Label::a_north:
  380. case Label::a_center:
  381. case Label::a_south:
  382. {
  383. tx0 = (wide - tWide) / 2;
  384. break;
  385. }
  386. // right
  387. case Label::a_northeast:
  388. case Label::a_east:
  389. case Label::a_southeast:
  390. {
  391. tx0 = wide - tWide;
  392. break;
  393. }
  394. }
  395. // y align text
  396. switch (_contentAlignment)
  397. {
  398. //top
  399. case Label::a_northwest:
  400. case Label::a_north:
  401. case Label::a_northeast:
  402. {
  403. ty0 = 0;
  404. break;
  405. }
  406. // center
  407. case Label::a_west:
  408. case Label::a_center:
  409. case Label::a_east:
  410. {
  411. ty0 = (tall - tTall) / 2;
  412. break;
  413. }
  414. // south
  415. case Label::a_southwest:
  416. case Label::a_south:
  417. case Label::a_southeast:
  418. {
  419. ty0 = tall - tTall;
  420. break;
  421. }
  422. }
  423. tx1 = tx0 + tWide;
  424. ty1 = ty0 + tTall;
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: overridden main drawing function for the panel
  428. //-----------------------------------------------------------------------------
  429. void Label::Paint()
  430. {
  431. int tx0, ty0, tx1, ty1;
  432. ComputeAlignment(tx0, ty0, tx1, ty1);
  433. // calculate who our associate is if we haven't already
  434. if (_associateName)
  435. {
  436. SetAssociatedControl(FindSiblingByName(_associateName));
  437. delete [] _associateName;
  438. _associateName = NULL;
  439. }
  440. int labelWide, labelTall;
  441. GetSize(labelWide, labelTall);
  442. int x = tx0, y = _textInset[1] + ty0;
  443. int imageYPos = 0; // a place to save the y offset for when we draw the disable version of the image
  444. // draw the set of images
  445. for (int i = 0; i < _imageDar.Count(); i++)
  446. {
  447. TImageInfo &imageInfo = _imageDar[i];
  448. IImage *image = imageInfo.image;
  449. if (!image)
  450. continue; // skip over null images
  451. // add the offset to x
  452. x += imageInfo.offset;
  453. // if this is the text image then add its inset to the left or from the right
  454. if (i == _textImageIndex)
  455. {
  456. switch ( _contentAlignment )
  457. {
  458. // left
  459. case Label::a_northwest:
  460. case Label::a_west:
  461. case Label::a_southwest:
  462. {
  463. x += _textInset[0];
  464. break;
  465. }
  466. // right
  467. case Label::a_northeast:
  468. case Label::a_east:
  469. case Label::a_southeast:
  470. {
  471. x -= _textInset[0];
  472. break;
  473. }
  474. }
  475. }
  476. // see if the image is in a fixed position
  477. if (imageInfo.xpos >= 0)
  478. {
  479. x = imageInfo.xpos;
  480. }
  481. // draw
  482. imageYPos = y;
  483. image->SetPos(x, y);
  484. // fix up y for center-aligned text
  485. if (_contentAlignment == Label::a_west || _contentAlignment == Label::a_center || _contentAlignment == Label::a_east)
  486. {
  487. int iw, it;
  488. image->GetSize(iw, it);
  489. if (it < (ty1 - ty0))
  490. {
  491. imageYPos = ((ty1 - ty0) - it) / 2 + y;
  492. image->SetPos(x, ((ty1 - ty0) - it) / 2 + y);
  493. }
  494. }
  495. // don't resize the image unless its too big
  496. if (imageInfo.width >= 0)
  497. {
  498. int w, t;
  499. image->GetSize(w, t);
  500. if (w > imageInfo.width)
  501. {
  502. image->SetSize(imageInfo.width, t);
  503. }
  504. }
  505. // if it's the basic text image then draw specially
  506. if (image == _textImage)
  507. {
  508. if (IsEnabled())
  509. {
  510. if (_associate.Get() && ipanel()->HasParent(input()->GetFocus(), _associate->GetVPanel()))
  511. {
  512. _textImage->SetColor(_associateColor);
  513. }
  514. else
  515. {
  516. _textImage->SetColor(GetFgColor());
  517. }
  518. _textImage->Paint();
  519. }
  520. else
  521. {
  522. // draw disabled version, with embossed look
  523. // offset image
  524. _textImage->SetPos(x + 1, imageYPos + 1);
  525. _textImage->SetColor(_disabledFgColor1);
  526. _textImage->Paint();
  527. surface()->DrawFlushText();
  528. // overlayed image
  529. _textImage->SetPos(x, imageYPos);
  530. _textImage->SetColor(_disabledFgColor2);
  531. _textImage->Paint();
  532. }
  533. }
  534. else
  535. {
  536. image->Paint();
  537. }
  538. // add the image size to x
  539. int wide, tall;
  540. image->GetSize(wide, tall);
  541. x += wide;
  542. }
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Purpose: Helper function, draws a simple line with dashing parameters
  546. //-----------------------------------------------------------------------------
  547. void Label::DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen)
  548. {
  549. // work out which way the line goes
  550. if ((x1 - x0) > (y1 - y0))
  551. {
  552. // x direction line
  553. while (1)
  554. {
  555. if (x0 + dashLen > x1)
  556. {
  557. // draw partial
  558. surface()->DrawFilledRect(x0, y0, x1, y1);
  559. }
  560. else
  561. {
  562. surface()->DrawFilledRect(x0, y0, x0 + dashLen, y1);
  563. }
  564. x0 += dashLen;
  565. if (x0 + gapLen > x1)
  566. break;
  567. x0 += gapLen;
  568. }
  569. }
  570. else
  571. {
  572. // y direction
  573. while (1)
  574. {
  575. if (y0 + dashLen > y1)
  576. {
  577. // draw partial
  578. surface()->DrawFilledRect(x0, y0, x1, y1);
  579. }
  580. else
  581. {
  582. surface()->DrawFilledRect(x0, y0, x1, y0 + dashLen);
  583. }
  584. y0 += dashLen;
  585. if (y0 + gapLen > y1)
  586. break;
  587. y0 += gapLen;
  588. }
  589. }
  590. }
  591. void Label::SetContentAlignment(Alignment alignment)
  592. {
  593. _contentAlignment=alignment;
  594. Repaint();
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Purpose: Size the width of the label to its contents - only works from in ApplySchemeSettings or PerformLayout()
  598. //-----------------------------------------------------------------------------
  599. void Label::SizeToContents()
  600. {
  601. int wide, tall;
  602. GetContentSize(wide, tall);
  603. SetSize(wide, tall);
  604. }
  605. //-----------------------------------------------------------------------------
  606. // Purpose: Set the font the text is drawn in
  607. //-----------------------------------------------------------------------------
  608. void Label::SetFont(HFont font)
  609. {
  610. _textImage->SetFont(font);
  611. Repaint();
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: Resond to resizing of the panel
  615. //-----------------------------------------------------------------------------
  616. void Label::OnSizeChanged(int wide, int tall)
  617. {
  618. InvalidateLayout();
  619. Panel::OnSizeChanged(wide, tall);
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Purpose: Get the font the textImage is drawn in.
  623. //-----------------------------------------------------------------------------
  624. HFont Label::GetFont()
  625. {
  626. return _textImage->GetFont();
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Set the foreground color of the Label
  630. //-----------------------------------------------------------------------------
  631. void Label::SetFgColor(Color color)
  632. {
  633. if (!(GetFgColor() == color))
  634. {
  635. BaseClass::SetFgColor(color);
  636. _textImage->SetColor(color);
  637. Repaint();
  638. }
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose: Get the foreground color of the Label
  642. //-----------------------------------------------------------------------------
  643. Color Label::GetFgColor()
  644. {
  645. Color clr = Panel::GetFgColor();
  646. return clr;
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Purpose: Set the foreground color 1 color of the Label
  650. //-----------------------------------------------------------------------------
  651. void Label::SetDisabledFgColor1(Color color)
  652. {
  653. _disabledFgColor1 = color;
  654. }
  655. //-----------------------------------------------------------------------------
  656. // Purpose:
  657. //-----------------------------------------------------------------------------
  658. void Label::SetDisabledFgColor2(Color color)
  659. {
  660. _disabledFgColor2 = color;
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose:
  664. //-----------------------------------------------------------------------------
  665. Color Label::GetDisabledFgColor1()
  666. {
  667. return _disabledFgColor1;
  668. }
  669. //-----------------------------------------------------------------------------
  670. // Purpose:
  671. //-----------------------------------------------------------------------------
  672. Color Label::GetDisabledFgColor2()
  673. {
  674. return _disabledFgColor2;
  675. }
  676. //-----------------------------------------------------------------------------
  677. // Purpose:
  678. //-----------------------------------------------------------------------------
  679. TextImage *Label::GetTextImage()
  680. {
  681. return _textImage;
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose:
  685. //-----------------------------------------------------------------------------
  686. bool Label::RequestInfo(KeyValues *outputData)
  687. {
  688. if (!stricmp(outputData->GetName(), "GetText"))
  689. {
  690. wchar_t wbuf[256];
  691. _textImage->GetText(wbuf, 255);
  692. outputData->SetWString("text", wbuf);
  693. return true;
  694. }
  695. return Panel::RequestInfo(outputData);
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Purpose: Sets the text from the message
  699. //-----------------------------------------------------------------------------
  700. void Label::OnSetText(KeyValues *params)
  701. {
  702. KeyValues *pkvText = params->FindKey("text", false);
  703. if (!pkvText)
  704. return;
  705. if (pkvText->GetDataType() == KeyValues::TYPE_STRING)
  706. {
  707. SetText(pkvText->GetString());
  708. }
  709. else if (pkvText->GetDataType() == KeyValues::TYPE_WSTRING)
  710. {
  711. SetText(pkvText->GetWString());
  712. }
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Purpose: Add an image to the list
  716. // returns the index the image was placed in
  717. //-----------------------------------------------------------------------------
  718. int Label::AddImage(IImage *image, int offset)
  719. {
  720. int newImage = _imageDar.AddToTail();
  721. _imageDar[newImage].image = image;
  722. _imageDar[newImage].offset = (short)offset;
  723. _imageDar[newImage].xpos = -1;
  724. _imageDar[newImage].width = -1;
  725. InvalidateLayout();
  726. return newImage;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose: removes all images from the list
  730. // user is responsible for the memory
  731. //-----------------------------------------------------------------------------
  732. void Label::ClearImages()
  733. {
  734. _imageDar.RemoveAll();
  735. _textImageIndex = -1;
  736. }
  737. void Label::ResetToSimpleTextImage()
  738. {
  739. ClearImages();
  740. _textImageIndex = AddImage(_textImage, 0);
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose: Multiple image handling
  744. // Images are drawn from left to right across the label, ordered by index
  745. // By default there is a TextImage in position 0
  746. // Set the contents of an IImage in the IImage array.
  747. //-----------------------------------------------------------------------------
  748. void Label::SetImageAtIndex(int index, IImage *image, int offset)
  749. {
  750. EnsureImageCapacity(index);
  751. // Assert( image );
  752. if ( _imageDar[index].image != image || _imageDar[index].offset != offset)
  753. {
  754. _imageDar[index].image = image;
  755. _imageDar[index].offset = (short)offset;
  756. InvalidateLayout();
  757. }
  758. }
  759. //-----------------------------------------------------------------------------
  760. // Purpose: Get an IImage in the IImage array.
  761. //-----------------------------------------------------------------------------
  762. IImage *Label::GetImageAtIndex(int index)
  763. {
  764. if ( _imageDar.IsValidIndex( index ) )
  765. return _imageDar[index].image;
  766. return NULL;
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose: Get the number of images in the array.
  770. //-----------------------------------------------------------------------------
  771. int Label::GetImageCount()
  772. {
  773. return _imageDar.Count();
  774. }
  775. //-----------------------------------------------------------------------------
  776. // Purpose: Move where the default text image is within the image array
  777. // (it starts in position 0)
  778. // Input : newIndex -
  779. // Output : int - the index the default text image was previously in
  780. //-----------------------------------------------------------------------------
  781. int Label::SetTextImageIndex(int newIndex)
  782. {
  783. if (newIndex == _textImageIndex)
  784. return _textImageIndex;
  785. EnsureImageCapacity(newIndex);
  786. int oldIndex = _textImageIndex;
  787. if ( _textImageIndex >= 0 )
  788. {
  789. _imageDar[_textImageIndex].image = NULL;
  790. }
  791. if (newIndex > -1)
  792. {
  793. _imageDar[newIndex].image = _textImage;
  794. }
  795. _textImageIndex = newIndex;
  796. return oldIndex;
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Purpose: Ensure that the maxIndex will be a valid index
  800. //-----------------------------------------------------------------------------
  801. void Label::EnsureImageCapacity(int maxIndex)
  802. {
  803. while (_imageDar.Size() <= maxIndex)
  804. {
  805. AddImage(NULL, 0);
  806. }
  807. }
  808. //-----------------------------------------------------------------------------
  809. // Purpose: Set the offset in pixels before the image
  810. //-----------------------------------------------------------------------------
  811. void Label::SetImagePreOffset(int index, int preOffset)
  812. {
  813. if (_imageDar.IsValidIndex(index) && _imageDar[index].offset != preOffset)
  814. {
  815. _imageDar[index].offset = (short)preOffset;
  816. InvalidateLayout();
  817. }
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Purpose: fixes the layout bounds of the image within the label
  821. //-----------------------------------------------------------------------------
  822. void Label::SetImageBounds(int index, int x, int width)
  823. {
  824. _imageDar[index].xpos = (short)x;
  825. _imageDar[index].width = (short)width;
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose: Labels can be associated with controls, and alter behaviour based on the associates behaviour
  829. // If the associate is disabled, so are we
  830. // If the associate has focus, we may alter how we draw
  831. // If we get a hotkey press or focus message, we forward the focus to the associate
  832. //-----------------------------------------------------------------------------
  833. void Label::SetAssociatedControl(Panel *control)
  834. {
  835. if (control != this)
  836. {
  837. _associate = control;
  838. }
  839. else
  840. {
  841. // don't let the associate ever be set to be ourself
  842. _associate = NULL;
  843. }
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose: Called after a panel requests focus to fix up the whole chain
  847. //-----------------------------------------------------------------------------
  848. void Label::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel)
  849. {
  850. if (_associate.Get() && subFocus == GetVPanel() && !IsBuildModeActive())
  851. {
  852. // we've received focus; pass the focus onto the associate instead
  853. _associate->RequestFocus();
  854. }
  855. else
  856. {
  857. BaseClass::OnRequestFocus(subFocus, defaultPanel);
  858. }
  859. }
  860. //-----------------------------------------------------------------------------
  861. // Purpose: sets custom settings from the scheme file
  862. //-----------------------------------------------------------------------------
  863. void Label::ApplySchemeSettings(IScheme *pScheme)
  864. {
  865. BaseClass::ApplySchemeSettings(pScheme);
  866. if (_fontOverrideName)
  867. {
  868. // use the custom specified font since we have one set
  869. SetFont(pScheme->GetFont(_fontOverrideName, IsProportional()));
  870. }
  871. if ( GetFont() == INVALID_FONT )
  872. {
  873. SetFont( pScheme->GetFont( "Default", IsProportional() ) );
  874. }
  875. if ( m_bWrap || m_bCenterWrap )
  876. {
  877. //tell it how big it is
  878. int wide, tall;
  879. Panel::GetSize(wide, tall);
  880. wide -= _textInset[0]; // take inset into account
  881. _textImage->SetSize(wide, tall);
  882. _textImage->RecalculateNewLinePositions();
  883. }
  884. else
  885. {
  886. // if you don't set the size of the image, many, many buttons will break - we might want to look into fixing this all over the place later
  887. int wide, tall;
  888. _textImage->GetContentSize(wide, tall);
  889. _textImage->SetSize(wide, tall);
  890. }
  891. if ( m_bAutoWideToContents )
  892. {
  893. m_bAutoWideDirty = true;
  894. HandleAutoSizing();
  895. }
  896. // clear out any the images, since they will have been invalidated
  897. for (int i = 0; i < _imageDar.Count(); i++)
  898. {
  899. IImage *image = _imageDar[i].image;
  900. if (!image)
  901. continue; // skip over null images
  902. if (i == _textImageIndex)
  903. continue;
  904. _imageDar[i].image = NULL;
  905. }
  906. SetDisabledFgColor1(GetSchemeColor("Label.DisabledFgColor1", pScheme));
  907. SetDisabledFgColor2(GetSchemeColor("Label.DisabledFgColor2", pScheme));
  908. SetBgColor(GetSchemeColor("Label.BgColor", pScheme));
  909. switch (_textColorState)
  910. {
  911. case CS_DULL:
  912. SetFgColor(GetSchemeColor("Label.TextDullColor", pScheme));
  913. break;
  914. case CS_BRIGHT:
  915. SetFgColor(GetSchemeColor("Label.TextBrightColor", pScheme));
  916. break;
  917. case CS_NORMAL:
  918. default:
  919. SetFgColor(GetSchemeColor("Label.TextColor", pScheme));
  920. break;
  921. }
  922. _associateColor = GetSchemeColor("Label.SelectedTextColor", pScheme);
  923. }
  924. //-----------------------------------------------------------------------------
  925. // Purpose:
  926. //-----------------------------------------------------------------------------
  927. void Label::GetSettings( KeyValues *outResourceData )
  928. {
  929. // panel settings
  930. Panel::GetSettings( outResourceData );
  931. // label settings
  932. char buf[256];
  933. _textImage->GetUnlocalizedText( buf, 255 );
  934. if (!strnicmp(buf, "#var_", 5))
  935. {
  936. // strip off the variable prepender on save
  937. outResourceData->SetString( "labelText", buf + 5 );
  938. }
  939. else
  940. {
  941. outResourceData->SetString( "labelText", buf );
  942. }
  943. const char *alignmentString = "";
  944. switch ( _contentAlignment )
  945. {
  946. case a_northwest: alignmentString = "north-west"; break;
  947. case a_north: alignmentString = "north"; break;
  948. case a_northeast: alignmentString = "north-east"; break;
  949. case a_center: alignmentString = "center"; break;
  950. case a_east: alignmentString = "east"; break;
  951. case a_southwest: alignmentString = "south-west"; break;
  952. case a_south: alignmentString = "south"; break;
  953. case a_southeast: alignmentString = "south-east"; break;
  954. case a_west:
  955. default: alignmentString = "west"; break;
  956. }
  957. outResourceData->SetString( "textAlignment", alignmentString );
  958. if (_associate)
  959. {
  960. outResourceData->SetString("associate", _associate->GetName());
  961. }
  962. outResourceData->SetInt("dulltext", (int)(_textColorState == CS_DULL));
  963. outResourceData->SetInt("brighttext", (int)(_textColorState == CS_BRIGHT));
  964. if (_fontOverrideName)
  965. {
  966. outResourceData->SetString("font", _fontOverrideName);
  967. }
  968. outResourceData->SetInt("wrap", ( m_bWrap ? 1 : 0 ));
  969. outResourceData->SetInt("centerwrap", ( m_bCenterWrap ? 1 : 0 ));
  970. if ( m_bUseProportionalInsets )
  971. {
  972. outResourceData->SetInt("textinsetx", scheme()->GetProportionalNormalizedValueEx( GetScheme(), _textInset[0] ) );
  973. outResourceData->SetInt("textinsety", _textInset[1]);
  974. }
  975. else
  976. {
  977. outResourceData->SetInt("textinsetx", _textInset[0]);
  978. outResourceData->SetInt("textinsety", _textInset[1]);
  979. }
  980. outResourceData->SetInt("auto_wide_tocontents", ( m_bAutoWideToContents ? 1 : 0 ));
  981. outResourceData->SetInt("use_proportional_insets", ( m_bUseProportionalInsets ? 1 : 0 ));
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. //-----------------------------------------------------------------------------
  986. void Label::ApplySettings( KeyValues *inResourceData )
  987. {
  988. BaseClass::ApplySettings( inResourceData );
  989. // label settings
  990. const char *labelText = inResourceData->GetString( "labelText", NULL );
  991. if ( labelText )
  992. {
  993. if (labelText[0] == '%' && labelText[strlen(labelText) - 1] == '%')
  994. {
  995. // it's a variable, set it to be a special variable localized string
  996. wchar_t unicodeVar[256];
  997. g_pVGuiLocalize->ConvertANSIToUnicode(labelText, unicodeVar, sizeof(unicodeVar));
  998. char var[256];
  999. _snprintf(var, sizeof(var), "#var_%s", labelText);
  1000. g_pVGuiLocalize->AddString(var + 1, unicodeVar, "");
  1001. SetText(var);
  1002. }
  1003. else
  1004. {
  1005. SetText(labelText);
  1006. }
  1007. }
  1008. // text alignment
  1009. const char *alignmentString = inResourceData->GetString( "textAlignment", "" );
  1010. int align = -1;
  1011. if ( !stricmp(alignmentString, "north-west") )
  1012. {
  1013. align = a_northwest;
  1014. }
  1015. else if ( !stricmp(alignmentString, "north") )
  1016. {
  1017. align = a_north;
  1018. }
  1019. else if ( !stricmp(alignmentString, "north-east") )
  1020. {
  1021. align = a_northeast;
  1022. }
  1023. else if ( !stricmp(alignmentString, "west") )
  1024. {
  1025. align = a_west;
  1026. }
  1027. else if ( !stricmp(alignmentString, "center") )
  1028. {
  1029. align = a_center;
  1030. }
  1031. else if ( !stricmp(alignmentString, "east") )
  1032. {
  1033. align = a_east;
  1034. }
  1035. else if ( !stricmp(alignmentString, "south-west") )
  1036. {
  1037. align = a_southwest;
  1038. }
  1039. else if ( !stricmp(alignmentString, "south") )
  1040. {
  1041. align = a_south;
  1042. }
  1043. else if ( !stricmp(alignmentString, "south-east") )
  1044. {
  1045. align = a_southeast;
  1046. }
  1047. if ( align != -1 )
  1048. {
  1049. SetContentAlignment( (Alignment)align );
  1050. }
  1051. // the control we are to be associated with may not have been created yet,
  1052. // so keep a pointer to it's name and calculate it when we can
  1053. const char *associateName = inResourceData->GetString("associate", "");
  1054. if (associateName[0] != 0)
  1055. {
  1056. int len = Q_strlen(associateName) + 1;
  1057. _associateName = new char[ len ];
  1058. Q_strncpy( _associateName, associateName, len );
  1059. }
  1060. if (inResourceData->GetInt("dulltext", 0) == 1)
  1061. {
  1062. SetTextColorState(CS_DULL);
  1063. }
  1064. else if (inResourceData->GetInt("brighttext", 0) == 1)
  1065. {
  1066. SetTextColorState(CS_BRIGHT);
  1067. }
  1068. else
  1069. {
  1070. SetTextColorState(CS_NORMAL);
  1071. }
  1072. // font settings
  1073. const char *overrideFont = inResourceData->GetString("font", "");
  1074. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  1075. if (*overrideFont)
  1076. {
  1077. delete [] _fontOverrideName;
  1078. int len = Q_strlen(overrideFont) + 1;
  1079. _fontOverrideName = new char[ len ];
  1080. Q_strncpy(_fontOverrideName, overrideFont, len );
  1081. SetFont(pScheme->GetFont(_fontOverrideName, IsProportional()));
  1082. }
  1083. else if (_fontOverrideName)
  1084. {
  1085. delete [] _fontOverrideName;
  1086. _fontOverrideName = NULL;
  1087. SetFont(pScheme->GetFont("Default", IsProportional()));
  1088. }
  1089. bool bWrapText = inResourceData->GetInt("centerwrap", 0) > 0;
  1090. SetCenterWrap( bWrapText );
  1091. m_bAutoWideToContents = inResourceData->GetInt("auto_wide_tocontents", 0) > 0;
  1092. bWrapText = inResourceData->GetInt("wrap", 0) > 0;
  1093. SetWrap( bWrapText );
  1094. int inset_x = inResourceData->GetInt("textinsetx", _textInset[0]);
  1095. int inset_y = inResourceData->GetInt("textinsety", _textInset[1]);
  1096. // Had to play it safe and add a new key for backwards compatibility
  1097. m_bUseProportionalInsets = inResourceData->GetInt("use_proportional_insets", 0) > 0;
  1098. if ( m_bUseProportionalInsets )
  1099. {
  1100. inset_x = scheme()->GetProportionalScaledValueEx( GetScheme(), inset_x );
  1101. }
  1102. SetTextInset( inset_x, inset_y );
  1103. bool bAllCaps = inResourceData->GetInt("allcaps", 0) > 0;
  1104. SetAllCaps( bAllCaps );
  1105. InvalidateLayout(true);
  1106. }
  1107. //-----------------------------------------------------------------------------
  1108. // Purpose: Returns a description of the label string
  1109. //-----------------------------------------------------------------------------
  1110. const char *Label::GetDescription( void )
  1111. {
  1112. static char buf[1024];
  1113. Q_snprintf(buf, sizeof(buf), "%s, string labelText, string associate, alignment textAlignment, int wrap, int dulltext, int brighttext, string font", BaseClass::GetDescription());
  1114. return buf;
  1115. }
  1116. //-----------------------------------------------------------------------------
  1117. // Purpose: If a label has images in _imageDar, the size
  1118. // must take those into account as well as the textImage part
  1119. // Textimage part will shrink ONLY if there is not enough room.
  1120. //-----------------------------------------------------------------------------
  1121. void Label::PerformLayout()
  1122. {
  1123. int wide, tall;
  1124. Panel::GetSize(wide, tall);
  1125. wide -= _textInset[0]; // take inset into account
  1126. // if we just have a textImage, this is trivial.
  1127. if (_imageDar.Count() == 1 && _textImageIndex == 0)
  1128. {
  1129. if ( m_bWrap || m_bCenterWrap )
  1130. {
  1131. int twide, ttall;
  1132. _textImage->GetContentSize(twide, ttall);
  1133. _textImage->SetSize(wide, ttall);
  1134. }
  1135. else
  1136. {
  1137. int twide, ttall;
  1138. _textImage->GetContentSize(twide, ttall);
  1139. // tell the textImage how much space we have to draw in
  1140. if ( wide < twide)
  1141. _textImage->SetSize(wide, ttall);
  1142. else
  1143. _textImage->SetSize(twide, ttall);
  1144. }
  1145. HandleAutoSizing();
  1146. HandleAutoSizing();
  1147. return;
  1148. }
  1149. // assume the images in the dar cannot be resized, and if
  1150. // the images + the textimage are too wide we shring the textimage part
  1151. if (_textImageIndex < 0)
  1152. return;
  1153. // get the size of the images
  1154. int widthOfImages = 0;
  1155. for (int i = 0; i < _imageDar.Count(); i++)
  1156. {
  1157. TImageInfo &imageInfo = _imageDar[i];
  1158. IImage *image = imageInfo.image;
  1159. if (!image)
  1160. continue; // skip over null images
  1161. if (i == _textImageIndex)
  1162. continue;
  1163. // add up the bounds
  1164. int iWide, iTall;
  1165. image->GetSize(iWide, iTall);
  1166. widthOfImages += iWide;
  1167. }
  1168. // so this is how much room we have to draw the textimage part
  1169. int spaceAvail = wide - widthOfImages;
  1170. // if we have no space at all just leave everything as is.
  1171. if (spaceAvail < 0)
  1172. return;
  1173. int twide, ttall;
  1174. _textImage->GetSize (twide, ttall);
  1175. // tell the textImage how much space we have to draw in
  1176. _textImage->SetSize(spaceAvail, ttall);
  1177. HandleAutoSizing();
  1178. }
  1179. void Label::SetWrap( bool bWrap )
  1180. {
  1181. m_bWrap = bWrap;
  1182. _textImage->SetWrap( m_bWrap );
  1183. InvalidateLayout();
  1184. }
  1185. void Label::SetCenterWrap( bool bWrap )
  1186. {
  1187. m_bCenterWrap = bWrap;
  1188. _textImage->SetCenterWrap( m_bCenterWrap );
  1189. InvalidateLayout();
  1190. }
  1191. void Label::SetAllCaps( bool bAllCaps )
  1192. {
  1193. m_bAllCaps = bAllCaps;
  1194. _textImage->SetAllCaps( m_bAllCaps );
  1195. InvalidateLayout();
  1196. }
  1197. void Label::HandleAutoSizing( void )
  1198. {
  1199. if ( m_bAutoWideDirty )
  1200. {
  1201. m_bAutoWideDirty = false;
  1202. // Only change our width to match our content
  1203. int wide, tall;
  1204. GetContentSize(wide, tall);
  1205. SetSize(wide, GetTall());
  1206. }
  1207. }