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.

352 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <math.h>
  9. #include <stdio.h>
  10. #include <vgui_controls/CircularProgressBar.h>
  11. #include <vgui_controls/Controls.h>
  12. #include <vgui/ILocalize.h>
  13. #include <vgui/IScheme.h>
  14. #include <vgui/ISurface.h>
  15. #include <KeyValues.h>
  16. #include "mathlib/mathlib.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include <tier0/memdbgon.h>
  19. using namespace vgui;
  20. DECLARE_BUILD_FACTORY( CircularProgressBar );
  21. //-----------------------------------------------------------------------------
  22. // Purpose: Constructor
  23. //-----------------------------------------------------------------------------
  24. CircularProgressBar::CircularProgressBar(Panel *parent, const char *panelName)
  25. : ProgressBar(parent, panelName)
  26. , m_bReverseProgress( false )
  27. {
  28. m_iProgressDirection = CircularProgressBar::PROGRESS_CW;
  29. for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
  30. {
  31. m_nTextureId[i] = -1;
  32. m_pszImageName[i] = NULL;
  33. m_lenImageName[i] = 0;
  34. }
  35. m_iStartSegment = 0;
  36. }
  37. //-----------------------------------------------------------------------------
  38. // Purpose: Destructor
  39. //-----------------------------------------------------------------------------
  40. CircularProgressBar::~CircularProgressBar()
  41. {
  42. for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
  43. {
  44. if ( vgui::surface() && m_nTextureId[i] )
  45. {
  46. vgui::surface()->DestroyTextureID( m_nTextureId[i] );
  47. m_nTextureId[i] = -1;
  48. }
  49. delete [] m_pszImageName[i];
  50. m_lenImageName[i] = 0;
  51. }
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose:
  55. //-----------------------------------------------------------------------------
  56. void CircularProgressBar::ApplySettings(KeyValues *inResourceData)
  57. {
  58. for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
  59. {
  60. delete [] m_pszImageName[i];
  61. m_pszImageName[i] = NULL;
  62. m_lenImageName[i] = 0;
  63. }
  64. const char *imageName = inResourceData->GetString("fg_image", "");
  65. if (*imageName)
  66. {
  67. SetFgImage( imageName );
  68. }
  69. imageName = inResourceData->GetString("bg_image", "");
  70. if (*imageName)
  71. {
  72. SetBgImage( imageName );
  73. }
  74. BaseClass::ApplySettings( inResourceData );
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. //-----------------------------------------------------------------------------
  79. void CircularProgressBar::ApplySchemeSettings(IScheme *pScheme)
  80. {
  81. BaseClass::ApplySchemeSettings(pScheme);
  82. SetFgColor(GetSchemeColor("CircularProgressBar.FgColor", pScheme));
  83. SetBgColor(GetSchemeColor("CircularProgressBar.BgColor", pScheme));
  84. SetBorder(NULL);
  85. for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
  86. {
  87. if ( m_pszImageName[i] && strlen( m_pszImageName[i] ) > 0 )
  88. {
  89. if ( m_nTextureId[i] == -1 )
  90. {
  91. m_nTextureId[i] = surface()->CreateNewTextureID();
  92. }
  93. surface()->DrawSetTextureFile( m_nTextureId[i], m_pszImageName[i], true, false);
  94. }
  95. }
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: sets an image by file name
  99. //-----------------------------------------------------------------------------
  100. void CircularProgressBar::SetImage(const char *imageName, progress_textures_t iPos)
  101. {
  102. const char *pszDir = "vgui/";
  103. int len = Q_strlen(imageName) + 1;
  104. len += strlen(pszDir);
  105. if ( m_pszImageName[iPos] && ( m_lenImageName[iPos] < len ) )
  106. {
  107. // If we already have a buffer, but it is too short, then free the buffer
  108. delete [] m_pszImageName[iPos];
  109. m_pszImageName[iPos] = NULL;
  110. m_lenImageName[iPos] = 0;
  111. }
  112. if ( !m_pszImageName[iPos] )
  113. {
  114. m_pszImageName[iPos] = new char[ len ];
  115. m_lenImageName[iPos] = len;
  116. }
  117. Q_snprintf( m_pszImageName[iPos], len, "%s%s", pszDir, imageName );
  118. InvalidateLayout(false, true); // force applyschemesettings to run
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. void CircularProgressBar::PaintBackground()
  124. {
  125. // If we don't have a Bg image, use the foreground
  126. int iTextureID = m_nTextureId[PROGRESS_TEXTURE_BG] != -1 ? m_nTextureId[PROGRESS_TEXTURE_BG] : m_nTextureId[PROGRESS_TEXTURE_FG];
  127. vgui::surface()->DrawSetTexture( iTextureID );
  128. vgui::surface()->DrawSetColor( GetBgColor() );
  129. int wide, tall;
  130. GetSize(wide, tall);
  131. vgui::surface()->DrawTexturedRect( 0, 0, wide, tall );
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose:
  135. //-----------------------------------------------------------------------------
  136. void CircularProgressBar::Paint()
  137. {
  138. float flProgress = GetProgress();
  139. float flEndAngle;
  140. flEndAngle = m_bReverseProgress ? ( 1.0 - flProgress ) : flProgress;
  141. flEndAngle = m_iProgressDirection == PROGRESS_CCW ? ( 1.0 - flEndAngle ) : flEndAngle;
  142. DrawCircleSegment( GetFgColor(), flEndAngle, ( m_iProgressDirection == PROGRESS_CW ) );
  143. }
  144. typedef struct
  145. {
  146. float minProgressRadians;
  147. float vert1x;
  148. float vert1y;
  149. float vert2x;
  150. float vert2y;
  151. int swipe_dir_x;
  152. int swipe_dir_y;
  153. } circular_progress_segment_t;
  154. namespace vgui
  155. {
  156. // This defines the properties of the 8 circle segments
  157. // in the circular progress bar.
  158. circular_progress_segment_t Segments[8] =
  159. {
  160. { 0.0, 0.5, 0.0, 1.0, 0.0, 1, 0 },
  161. { M_PI * 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 },
  162. { M_PI * 0.5, 1.0, 0.5, 1.0, 1.0, 0, 1 },
  163. { M_PI * 0.75, 1.0, 1.0, 0.5, 1.0, -1, 0 },
  164. { M_PI, 0.5, 1.0, 0.0, 1.0, -1, 0 },
  165. { M_PI * 1.25, 0.0, 1.0, 0.0, 0.5, 0, -1 },
  166. { M_PI * 1.5, 0.0, 0.5, 0.0, 0.0, 0, -1 },
  167. { M_PI * 1.75, 0.0, 0.0, 0.5, 0.0, 1, 0 },
  168. };
  169. };
  170. #define SEGMENT_ANGLE ( M_PI / 4 )
  171. // function to draw from A to B degrees, with a direction
  172. // we draw starting from the top ( 0 progress )
  173. void CircularProgressBar::DrawCircleSegment( Color c, float flEndProgress, bool bClockwise )
  174. {
  175. if ( m_nTextureId[PROGRESS_TEXTURE_FG] == -1 )
  176. return;
  177. int wide, tall;
  178. GetSize(wide, tall);
  179. float flWide = (float)wide;
  180. float flTall = (float)tall;
  181. float flHalfWide = (float)wide / 2;
  182. float flHalfTall = (float)tall / 2;
  183. vgui::surface()->DrawSetTexture( m_nTextureId[PROGRESS_TEXTURE_FG] );
  184. vgui::surface()->DrawSetColor( c );
  185. // if we want to progress CCW, reverse a few things
  186. if ( !bClockwise )
  187. {
  188. float flEndProgressRadians = flEndProgress * M_PI * 2;
  189. int i;
  190. for ( i=0;i<8;i++ )
  191. {
  192. float segmentRadiansMin = Segments[i].minProgressRadians;
  193. float segmentRadiansMax = segmentRadiansMin + SEGMENT_ANGLE;
  194. if ( flEndProgressRadians < segmentRadiansMax )
  195. {
  196. vgui::Vertex_t v[3];
  197. // vert 0 is ( 0.5, 0.5 )
  198. v[0].m_Position.Init( flHalfWide, flHalfTall );
  199. v[0].m_TexCoord.Init( 0.5f, 0.5f );
  200. float flInternalProgress = segmentRadiansMax - flEndProgressRadians;
  201. if ( flInternalProgress < SEGMENT_ANGLE )
  202. {
  203. // Calc how much of this slice we should be drawing
  204. flInternalProgress = SEGMENT_ANGLE - flInternalProgress;
  205. if ( i % 2 == 1 )
  206. {
  207. flInternalProgress = SEGMENT_ANGLE - flInternalProgress;
  208. }
  209. float flTan = tan(flInternalProgress);
  210. float flDeltaX, flDeltaY;
  211. if ( i % 2 == 1 )
  212. {
  213. flDeltaX = ( flHalfWide - flHalfTall * flTan ) * Segments[i].swipe_dir_x;
  214. flDeltaY = ( flHalfTall - flHalfWide * flTan ) * Segments[i].swipe_dir_y;
  215. }
  216. else
  217. {
  218. flDeltaX = flHalfTall * flTan * Segments[i].swipe_dir_x;
  219. flDeltaY = flHalfWide * flTan * Segments[i].swipe_dir_y;
  220. }
  221. v[1].m_Position.Init( Segments[i].vert1x * flWide + flDeltaX, Segments[i].vert1y * flTall + flDeltaY );
  222. v[1].m_TexCoord.Init( Segments[i].vert1x + ( flDeltaX / flHalfWide ) * 0.5, Segments[i].vert1y + ( flDeltaY / flHalfTall ) * 0.5 );
  223. }
  224. else
  225. {
  226. // full segment, easy calculation
  227. v[1].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert1x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert1y - 0.5 ) );
  228. v[1].m_TexCoord.Init( Segments[i].vert1x, Segments[i].vert1y );
  229. }
  230. // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y )
  231. v[2].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert2x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert2y - 0.5 ) );
  232. v[2].m_TexCoord.Init( Segments[i].vert2x, Segments[i].vert2y );
  233. vgui::surface()->DrawTexturedPolygon( 3, v );
  234. }
  235. }
  236. return;
  237. }
  238. float flEndProgressRadians = flEndProgress * M_PI * 2;
  239. int cur_wedge = m_iStartSegment;
  240. for ( int i=0;i<8;i++ )
  241. {
  242. if ( flEndProgressRadians > Segments[cur_wedge].minProgressRadians)
  243. {
  244. vgui::Vertex_t v[3];
  245. // vert 0 is ( 0.5, 0.5 )
  246. v[0].m_Position.Init( flHalfWide, flHalfTall );
  247. v[0].m_TexCoord.Init( 0.5f, 0.5f );
  248. float flInternalProgress = flEndProgressRadians - Segments[cur_wedge].minProgressRadians;
  249. if ( flInternalProgress < SEGMENT_ANGLE )
  250. {
  251. // Calc how much of this slice we should be drawing
  252. if ( i % 2 == 1 )
  253. {
  254. flInternalProgress = SEGMENT_ANGLE - flInternalProgress;
  255. }
  256. float flTan = tan(flInternalProgress);
  257. float flDeltaX, flDeltaY;
  258. if ( i % 2 == 1 )
  259. {
  260. flDeltaX = ( flHalfWide - flHalfTall * flTan ) * Segments[i].swipe_dir_x;
  261. flDeltaY = ( flHalfTall - flHalfWide * flTan ) * Segments[i].swipe_dir_y;
  262. }
  263. else
  264. {
  265. flDeltaX = flHalfTall * flTan * Segments[i].swipe_dir_x;
  266. flDeltaY = flHalfWide * flTan * Segments[i].swipe_dir_y;
  267. }
  268. v[2].m_Position.Init( Segments[i].vert1x * flWide + flDeltaX, Segments[i].vert1y * flTall + flDeltaY );
  269. v[2].m_TexCoord.Init( Segments[i].vert1x + ( flDeltaX / flHalfWide ) * 0.5, Segments[i].vert1y + ( flDeltaY / flHalfTall ) * 0.5 );
  270. }
  271. else
  272. {
  273. // full segment, easy calculation
  274. v[2].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert2x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert2y - 0.5 ) );
  275. v[2].m_TexCoord.Init( Segments[i].vert2x, Segments[i].vert2y );
  276. }
  277. // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y )
  278. v[1].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert1x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert1y - 0.5 ) );
  279. v[1].m_TexCoord.Init( Segments[i].vert1x, Segments[i].vert1y );
  280. vgui::surface()->DrawTexturedPolygon( 3, v );
  281. }
  282. cur_wedge++;
  283. if ( cur_wedge >= 8)
  284. cur_wedge = 0;
  285. }
  286. }