﻿//+------------------------------------------------------------------+
//|                                      ToolsPalette_Primitives.mqh |
//|                            Copyright 2026, Allan Munene Mutiiria.|
//|                                    https://t.me/Forex_Algo_Trader|
//+------------------------------------------------------------------+
#ifndef TOOLS_PALETTE_PRIMITIVES_MQH
#define TOOLS_PALETTE_PRIMITIVES_MQH

#include <Canvas/Canvas.mqh>

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input int    BorderWidth        = 1;    // Border Width (px)
input double BackgroundOpacity  = 0.92; // Background Opacity (0.0 - 1.0)
input bool   StartDark          = false; // Start In Dark Theme

//+------------------------------------------------------------------+
//| Theme color set structure                                        |
//+------------------------------------------------------------------+
struct ThemeColorSet
  {
   color sidebarBackground;         // Sidebar panel fill color
   color sidebarBorder;             // Sidebar outer border color
   color buttonHoverBackground;     // Button fill on hover
   color buttonActiveBackground;    // Button fill when active/pressed
   color buttonIconColor;           // Icon tint in normal state
   color buttonIconActiveColor;     // Icon tint when button is active
   color flyoutBackground;          // Flyout panel fill color
   color flyoutBorder;              // Flyout outer border color
   color flyoutItemHoverBackground; // Flyout row fill on hover
   color flyoutTextColor;           // Flyout item label text color
   color flyoutTextActiveColor;     // Flyout item label text when active
   color flyoutTitleColor;          // Flyout section title text color
   color gripDotsColor;             // Drag-grip dot fill color
   color closeButtonHoverColor;     // Close button tint on hover
   color themeButtonHoverColor;     // Theme-toggle button tint on hover
   color separatorColor;            // Divider line color
   color accentBarColor;            // Accent / highlight bar color
   color scrollArrowColor;          // Scroll arrow fill in normal state
   color scrollArrowHoverColor;     // Scroll arrow fill on hover
  };

//+------------------------------------------------------------------+
//| Convert color + percentage opacity to ARGB uint                  |
//+------------------------------------------------------------------+
uint ColorWithPercentOpacity(color c, int pct)
  {
   //--- Clamp percentage to valid range
   if(pct < 0)   pct = 0;
   if(pct > 100) pct = 100;
   //--- Map percentage to 0-255 alpha byte
   const uchar alpha = (uchar)((255 * pct) / 100);
   //--- Return ARGB with computed alpha
   return ColorToARGB(c, alpha);
  }

//+------------------------------------------------------------------+
//| Alpha-composite a single pixel onto the canvas (source-over)     |
//+------------------------------------------------------------------+
void WidgetBlendPixel(CCanvas &canvas, int x, int y, uint srcArgb)
  {
   //--- Reject pixels outside canvas bounds
   if(x < 0 || y < 0 || x >= canvas.Width() || y >= canvas.Height()) return;
   //--- Extract source alpha
   const uchar sa = (uchar)((srcArgb >> 24) & 0xFF);
   //--- Skip fully transparent pixels
   if(sa == 0) return;
   //--- Write opaque pixels directly without blending overhead
   if(sa == 255) { canvas.PixelSet(x, y, srcArgb); return; }
   //--- Read destination pixel for compositing
   const uint dst = canvas.PixelGet(x, y);
   //--- Unpack destination alpha
   const uchar da = (uchar)((dst >> 24) & 0xFF);
   //--- Unpack source RGB channels
   const int sr = (int)((srcArgb >> 16) & 0xFF);
   const int sg = (int)((srcArgb >>  8) & 0xFF);
   const int sb = (int)( srcArgb        & 0xFF);
   //--- Unpack destination RGB channels
   const int dr = (int)((dst >> 16) & 0xFF);
   const int dg = (int)((dst >>  8) & 0xFF);
   const int db = (int)( dst        & 0xFF);
   //--- Compute output alpha via source-over formula
   const int oa = sa + (int)da * (255 - sa) / 255;
   //--- Discard pixel if resulting alpha is zero
   if(oa <= 0) { canvas.PixelSet(x, y, 0); return; }
   //--- Precompute (1 - srcAlpha) factor for destination contribution
   const int oneMinusSA = 255 - sa;
   //--- Blend each channel proportionally
   const int r = (sr * sa + dr * (int)da * oneMinusSA / 255) / oa;
   const int g = (sg * sa + dg * (int)da * oneMinusSA / 255) / oa;
   const int b = (sb * sa + db * (int)da * oneMinusSA / 255) / oa;
   //--- Pack blended channels back into ARGB and write
   const uint outArgb = ((uint)oa << 24) | ((uint)(uchar)r << 16)
                      | ((uint)(uchar)g <<  8) | (uint)(uchar)b;
   canvas.PixelSet(x, y, outArgb);
  }

//+------------------------------------------------------------------+
//| Draw an anti-aliased thick line between two points              |
//+------------------------------------------------------------------+
void WidgetThickLineAA(CCanvas &canvas,
                        int x0, int y0, int x1, int y1,
                        int thickness, uint argb)
  {
   //--- Clamp thickness to supported range
   if(thickness < 1) thickness = 1;
   if(thickness > 4) thickness = 4;

   if(y0 == y1)
     {
      //--- Compute horizontal span limits
      const int xL = MathMin(x0, x1);
      const int xR = MathMax(x0, x1);
      //--- Center the stroke vertically around y0
      const int yTop = y0 - thickness / 2;
      const int yBot = yTop + thickness - 1;
      //--- Fill every pixel in the aligned rectangle
      for(int yy = yTop; yy <= yBot; yy++)
         for(int xx = xL; xx <= xR; xx++)
            WidgetBlendPixel(canvas, xx, yy, argb);
      return;
     }
   if(x0 == x1)
     {
      //--- Compute vertical span limits
      const int yT = MathMin(y0, y1);
      const int yB = MathMax(y0, y1);
      //--- Center the stroke horizontally around x0
      const int xL = x0 - thickness / 2;
      const int xR = xL + thickness - 1;
      //--- Fill every pixel in the aligned rectangle
      for(int xx = xL; xx <= xR; xx++)
         for(int yy = yT; yy <= yB; yy++)
            WidgetBlendPixel(canvas, xx, yy, argb);
      return;
     }

   //--- General diagonal path — true AA via signed distance + subpixel coverage
   const double halfT = (double)thickness / 2.0;
   //--- Shift pixel centers to their geometric midpoints
   const double ax = (double)x0 + 0.5;
   const double ay = (double)y0 + 0.5;
   const double bx = (double)x1 + 0.5;
   const double by = (double)y1 + 0.5;
   //--- Compute segment direction vector
   const double dx = bx - ax;
   const double dy = by - ay;
   const double lenSq = dx * dx + dy * dy;
   //--- Skip degenerate zero-length segments
   if(lenSq < 1e-9) return;
   //--- Compute bounding box with padding for AA radius
   const double pad = halfT + 1.0;
   const int bbL = (int)MathFloor(MathMin(ax, bx) - pad);
   const int bbT = (int)MathFloor(MathMin(ay, by) - pad);
   const int bbR = (int)MathCeil (MathMax(ax, bx) + pad);
   const int bbB = (int)MathCeil (MathMax(ay, by) + pad);
   //--- Extract base alpha and RGB from input color
   const uchar bA  = (uchar)((argb >> 24) & 0xFF);
   const uint  rgb = argb & 0x00FFFFFF;
   //--- Set up 4x4 subpixel grid
   const int    sub   = 4;
   const double step  = 1.0 / sub;
   const int    subSq = sub * sub;

   //--- Iterate over every pixel in the bounding box
   for(int py = bbT; py <= bbB; py++)
     {
      for(int px = bbL; px <= bbR; px++)
        {
         //--- Project pixel center onto segment to find closest point
         const double pcx = (double)px + 0.5;
         const double pcy = (double)py + 0.5;
         double t = ((pcx - ax) * dx + (pcy - ay) * dy) / lenSq;
         //--- Clamp projection to segment endpoints
         if(t < 0.0) t = 0.0;
         if(t > 1.0) t = 1.0;
         const double projX = ax + t * dx;
         const double projY = ay + t * dy;
         //--- Compute perpendicular distance to segment
         const double pdx = pcx - projX;
         const double pdy = pcy - projY;
         const double centerDist = MathSqrt(pdx * pdx + pdy * pdy);
         //--- Skip pixels fully outside stroke radius + AA margin
         if(centerDist > halfT + 1.0) continue;
         //--- Write fully covered interior pixels immediately
         if(centerDist <= halfT - 1.0)
           {
            WidgetBlendPixel(canvas, px, py, argb);
            continue;
           }
         //--- Count subpixel samples inside the stroke for AA coverage
         int inside = 0;
         for(int sy = 0; sy < sub; sy++)
           {
            for(int sx = 0; sx < sub; sx++)
              {
               //--- Sample subpixel position
               const double sx_ = (double)px + (sx + 0.5) * step;
               const double sy_ = (double)py + (sy + 0.5) * step;
               //--- Project subpixel onto segment
               double st = ((sx_ - ax) * dx + (sy_ - ay) * dy) / lenSq;
               if(st < 0.0) st = 0.0;
               if(st > 1.0) st = 1.0;
               const double spx = ax + st * dx;
               const double spy = ay + st * dy;
               const double sdx = sx_ - spx;
               const double sdy = sy_ - spy;
               //--- Count subpixel if inside half-thickness radius
               if(sdx * sdx + sdy * sdy <= halfT * halfT) inside++;
              }
           }
         //--- Skip pixels with zero subpixel coverage
         if(inside == 0) continue;
         //--- Write pixel with alpha proportional to coverage fraction
         const uint covArgb = (((uint)(uchar)((int)bA * inside / subSq)) << 24) | rgb;
         WidgetBlendPixel(canvas, px, py, covArgb);
        }
     }
  }

//+------------------------------------------------------------------+
//| Build on/off pixel-length pattern for a given line style         |
//+------------------------------------------------------------------+
int BuildLineStylePattern(int lineStyle, int lineWidth, int &outPattern[])
  {
   //--- Clamp line width to valid range
   if(lineWidth < 1) lineWidth = 1;
   if(lineWidth > 4) lineWidth = 4;
   switch(lineStyle)
     {
      case 1: // Dashed style
         ArrayResize(outPattern, 2);
         outPattern[0] = 6; outPattern[1] = 4;
         return 2;
      case 2: // Dotted style — gap and dash scale with width
         ArrayResize(outPattern, 2);
         outPattern[0] = lineWidth * 2; outPattern[1] = lineWidth * 2;
         return 2;
      case 3: // Dash-dot style
         ArrayResize(outPattern, 4);
         outPattern[0] = 6; outPattern[1] = 3;
         outPattern[2] = lineWidth; outPattern[3] = 3;
         return 4;
      default: // Solid — caller skips dashed rendering entirely
         ArrayResize(outPattern, 0);
         return 0;
     }
  }

//+------------------------------------------------------------------+
//| Draw a patterned anti-aliased line segment                       |
//+------------------------------------------------------------------+
void WidgetDashedLineAA(CCanvas &canvas,
                         int x0, int y0, int x1, int y1,
                         int thickness, uint argb,
                         int &pattern[])
  {
   //--- Validate pattern and thickness before proceeding
   const int patLen = ArraySize(pattern);
   if(patLen < 2 || thickness < 1) return;
   //--- Shift endpoints to pixel-center coordinates
   const double ax = (double)x0 + 0.5;
   const double ay = (double)y0 + 0.5;
   const double bx = (double)x1 + 0.5;
   const double by = (double)y1 + 0.5;
   //--- Compute segment direction and total length
   const double dx = bx - ax;
   const double dy = by - ay;
   const double totalLen = MathSqrt(dx * dx + dy * dy);
   //--- Skip degenerate zero-length segment
   if(totalLen < 1e-6) return;
   //--- Compute unit direction vector
   const double ux = dx / totalLen;
   const double uy = dy / totalLen;

   //--- Walk segment, drawing each "on" sub-segment from the pattern
   int    patIdx   = 0;
   double traveled = 0.0;
   while(traveled < totalLen)
     {
      //--- Fetch current pattern entry length, wrapping modulo patLen
      double segLen = (double)pattern[patIdx % patLen];
      //--- Skip zero-length pattern entries
      if(segLen <= 0.0) { patIdx++; continue; }
      //--- Clip final segment to remaining line length
      if(traveled + segLen > totalLen) segLen = totalLen - traveled;
      //--- Compute sub-segment start and end in float coordinates
      const double sx = ax + ux * traveled;
      const double sy = ay + uy * traveled;
      const double ex = ax + ux * (traveled + segLen);
      const double ey = ay + uy * (traveled + segLen);
      //--- Draw only even-indexed (on) pattern entries
      if((patIdx % 2) == 0)
        {
         //--- Convert float coords back to integer pixel coordinates
         const int isx = (int)MathRound(sx - 0.5);
         const int isy = (int)MathRound(sy - 0.5);
         const int iex = (int)MathRound(ex - 0.5);
         const int iey = (int)MathRound(ey - 0.5);
         WidgetThickLineAA(canvas, isx, isy, iex, iey, thickness, argb);
        }
      //--- Advance traveled distance and pattern index
      traveled += segLen;
      patIdx++;
     }
  }

//+------------------------------------------------------------------+
//| CLASS 1 — Low-level canvas pixel and shape primitives            |
//+------------------------------------------------------------------+
class CCanvasPrimitives
  {
protected:
   void BlendPixelSet(CCanvas &canvas, int x, int y, uint sourceARGB);                             // Blend pixel with alpha compositing
   void DownsampleCanvas(CCanvas &dst, CCanvas &src, int factor);                                   // Downsample high-res to lower-res canvas
   void FillCornerQuadrantHR(CCanvas &canvas, int cx, int cy, int radius, uint argb,
                              int signX, int signY);                                                 // Fill one rounded-rect corner quadrant
   void FillRoundRectHR(CCanvas &canvas, int x, int y, int w, int h, int radius, uint argb);       // Fill fully rounded rectangle
   void FillSelectiveRoundRectHR(CCanvas &canvas, int x, int y, int w, int h, int radius, uint argb,
                                  bool rTL, bool rTR, bool rBL, bool rBR);                          // Fill rounded rect with per-corner control
   void FillTriangleHR(CCanvas &canvas, int x0, int y0, int x1, int y1, int x2, int y2, uint argb); // Fill triangle via scanline rasterization
   void FillQuadrilateralBorder(CCanvas &canvas, double &vx[], double &vy[], uint argb);            // Fill quadrilateral via scanline rasterization
   void DrawBorderEdge(CCanvas &canvas, double x0, double y0, double x1, double y1,
                       int thickness, uint argb);                                                    // Draw single thick border edge
   bool IsAngleBetween(double angle, double startAngle, double endAngle);                           // Test whether angle lies within arc range
   void DrawCornerArc(CCanvas &canvas, int cx, int cy, int radius, int thickness, uint argb,
                      double startAngle, double endAngle);                                           // Draw AA corner arc with thickness
   void DrawSelectiveRoundRectBorderHR(CCanvas &canvas, int x, int y, int w, int h, int radius,
                                        uint argb, int thickness,
                                        bool rTL, bool rTR, bool rBL, bool rBR);                    // Draw rounded-rect border with per-corner control
   void DrawBresenhamLine(CCanvas &canvas, int x0, int y0, int x1, int y1, uint argb);             // Draw AA line via Wu's algorithm
   void DrawThickLine(CCanvas &canvas, int x0, int y0, int x1, int y1, int thickness, uint argb); // Draw SSAA thick line
   void FillCircleAA(CCanvas &canvas, int cx, int cy, int radius, uint argb);                      // Fill circle with AA edge
   void DrawCircleBorderAA(CCanvas &canvas, int cx, int cy, int radius, int thickness, uint argb); // Draw AA circle border
  };

//+------------------------------------------------------------------+
//| Blend source pixel onto canvas using alpha compositing           |
//+------------------------------------------------------------------+
void CCanvasPrimitives::BlendPixelSet(CCanvas &canvas, int x, int y, uint src)
  {
   //--- Reject out-of-bounds coordinates
   if(x < 0 || x >= canvas.Width() || y < 0 || y >= canvas.Height()) return;
   //--- Read destination pixel
   uint dst = canvas.PixelGet(x, y);
   //--- Unpack source ARGB channels as normalized floats
   double sA = ((src >> 24) & 0xFF) / 255.0, sR = ((src >> 16) & 0xFF) / 255.0;
   double sG = ((src >>  8) & 0xFF) / 255.0, sB = ( src        & 0xFF) / 255.0;
   //--- Unpack destination ARGB channels as normalized floats
   double dA = ((dst >> 24) & 0xFF) / 255.0, dR = ((dst >> 16) & 0xFF) / 255.0;
   double dG = ((dst >>  8) & 0xFF) / 255.0, dB = ( dst        & 0xFF) / 255.0;
   //--- Compute output alpha via source-over formula
   double oA = sA + dA * (1.0 - sA);
   //--- Write transparent pixel when output alpha is zero
   if(oA == 0.0) { canvas.PixelSet(x, y, 0); return; }
   //--- Pack and write blended ARGB result
   canvas.PixelSet(x, y,
      ((uint)(uchar)(oA * 255 + 0.5) << 24) |
      ((uint)(uchar)((sR * sA + dR * dA * (1.0 - sA)) / oA * 255 + 0.5) << 16) |
      ((uint)(uchar)((sG * sA + dG * dA * (1.0 - sA)) / oA * 255 + 0.5) <<  8) |
       (uint)(uchar)((sB * sA + dB * dA * (1.0 - sA)) / oA * 255 + 0.5));
  }

//+------------------------------------------------------------------+
//| Downsample high-res canvas into destination by averaging pixels  |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DownsampleCanvas(CCanvas &dst, CCanvas &src, int factor)
  {
   //--- Cache destination dimensions and squared sample count
   int dW = dst.Width(), dH = dst.Height(), ss2 = factor * factor;
   //--- Iterate over every destination pixel
   for(int py = 0; py < dH; py++)
      for(int px = 0; px < dW; px++)
        {
         //--- Accumulate channel sums for the sample block
         double sA = 0, sR = 0, sG = 0, sB = 0, wc = 0;
         for(int dy = 0; dy < factor; dy++)
            for(int dx = 0; dx < factor; dx++)
              {
               //--- Map destination pixel to source sample coordinates
               int sx = px * factor + dx, sy = py * factor + dy;
               //--- Skip samples that fall outside source bounds
               if(sx >= src.Width() || sy >= src.Height()) continue;
               //--- Read source pixel and extract alpha
               uint p = src.PixelGet(sx, sy); uchar a = (uchar)((p >> 24) & 0xFF);
               sA += a;
               //--- Accumulate weighted RGB only for non-transparent pixels
               if(a > 0) { sR += (p >> 16) & 0xFF; sG += (p >> 8) & 0xFF; sB += p & 0xFF; wc += 1.0; }
              }
         //--- Compute averaged output alpha
         uchar fa = (uchar)(sA / ss2);
         //--- Write transparent pixel if no coverage
         if(fa == 0 || wc == 0) { dst.PixelSet(px, py, 0); continue; }
         //--- Write averaged ARGB pixel to destination
         dst.PixelSet(px, py, ((uint)fa << 24) | ((uint)(uchar)(sR / wc) << 16) |
                               ((uint)(uchar)(sG / wc) << 8) | (uint)(uchar)(sB / wc));
        }
  }

//+------------------------------------------------------------------+
//| Fill one corner quadrant of a rounded rect at high resolution    |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillCornerQuadrantHR(CCanvas &canvas, int cx, int cy, int radius, uint argb, int signX, int signY)
  {
   //--- Cache radius as float and extract base alpha and RGB
   double rd = (double)radius;
   uchar  bA = (uchar)((argb >> 24) & 0xFF);
   uint   rgb = argb & 0x00FFFFFF;
   //--- Set up 4x4 subpixel AA grid
   int sub = 4; double step = 1.0 / sub; int subSq = sub * sub;
   //--- Iterate over the local patch around the corner center
   for(int dy = -(radius + 1); dy <= (radius + 1); dy++)
      for(int dx = -(radius + 1); dx <= (radius + 1); dx++)
        {
         //--- Reject pixels outside the correct quadrant
         bool inQ = ((signX > 0) ? (dx >= 0) : (dx <= 0)) && ((signY > 0) ? (dy >= 0) : (dy <= 0));
         if(!inQ) continue;
         //--- Compute distance from corner center
         double dist = MathSqrt((double)(dx * dx + dy * dy));
         //--- Skip pixels fully outside the arc radius
         if(dist > rd + 1.0) continue;
         //--- Write fully covered interior pixels directly
         if(dist <= rd - 1.0) { canvas.PixelSet(cx + dx, cy + dy, argb); continue; }
         //--- Count subpixel samples inside the arc for AA
         int inside = 0;
         for(int sy = 0; sy < sub; sy++)
            for(int sx = 0; sx < sub; sx++)
              {
               //--- Compute subpixel offset from pixel corner
               double sdx = (double)dx - 0.5 + (sx + 0.5) * step;
               double sdy = (double)dy - 0.5 + (sy + 0.5) * step;
               if(sdx * sdx + sdy * sdy <= rd * rd) inside++;
              }
         //--- Skip pixels with zero subpixel coverage
         if(inside == 0) continue;
         //--- Blend AA boundary pixel with proportional alpha
         BlendPixelSet(canvas, cx + dx, cy + dy, (((uint)(uchar)((int)bA * inside / subSq)) << 24) | rgb);
        }
  }

//+------------------------------------------------------------------+
//| Fill a fully rounded rectangle at high resolution                |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillRoundRectHR(CCanvas &canvas, int x, int y, int w, int h, int radius, uint argb)
  {
   //--- Clamp radius so it does not exceed half the shorter dimension
   radius = MathMin(radius, MathMin(w / 2, h / 2));
   //--- Fall back to plain rectangle for zero or negative radius
   if(radius <= 0) { canvas.FillRectangle(x, y, x + w - 1, y + h - 1, argb); return; }
   //--- Fill the horizontal center band
   canvas.FillRectangle(x + radius, y, x + w - radius - 1, y + h - 1, argb);
   //--- Fill the left vertical band
   canvas.FillRectangle(x, y + radius, x + radius - 1, y + h - radius - 1, argb);
   //--- Fill the right vertical band
   canvas.FillRectangle(x + w - radius, y + radius, x + w - 1, y + h - radius - 1, argb);
   //--- Fill all four rounded corners
   FillCornerQuadrantHR(canvas, x + radius,     y + radius,     radius, argb, -1, -1);
   FillCornerQuadrantHR(canvas, x + w - radius, y + radius,     radius, argb,  1, -1);
   FillCornerQuadrantHR(canvas, x + radius,     y + h - radius, radius, argb, -1,  1);
   FillCornerQuadrantHR(canvas, x + w - radius, y + h - radius, radius, argb,  1,  1);
  }

//+------------------------------------------------------------------+
//| Fill rounded rectangle with per-corner rounding at high res      |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillSelectiveRoundRectHR(CCanvas &canvas, int x, int y, int w, int h, int radius, uint argb,
                                                  bool rTL, bool rTR, bool rBL, bool rBR)
  {
   //--- Clamp radius to half the shorter dimension
   radius = MathMin(radius, MathMin(w / 2, h / 2));
   //--- Fall back to plain rectangle when radius is not positive
   if(radius <= 0) { canvas.FillRectangle(x, y, x + w - 1, y + h - 1, argb); return; }
   //--- Fill horizontal center band spanning full width minus corner regions
   canvas.FillRectangle(x + radius, y, x + w - radius - 1, y + h - 1, argb);
   //--- Fill left vertical band, shrinking top/bottom for rounded corners
   canvas.FillRectangle(x, y + (rTL ? radius : 0), x + radius - 1, y + h - 1 - (rBL ? radius : 0), argb);
   //--- Fill right vertical band, shrinking top/bottom for rounded corners
   canvas.FillRectangle(x + w - radius, y + (rTR ? radius : 0), x + w - 1, y + h - 1 - (rBR ? radius : 0), argb);
   //--- Handle top-left: rounded arc or plain square corner
   if(rTL) FillCornerQuadrantHR(canvas, x + radius,     y + radius,     radius, argb, -1, -1);
   else     canvas.FillRectangle(x, y, x + radius - 1, y + radius - 1, argb);
   //--- Handle top-right: rounded arc or plain square corner
   if(rTR) FillCornerQuadrantHR(canvas, x + w - radius, y + radius,     radius, argb,  1, -1);
   else     canvas.FillRectangle(x + w - radius, y, x + w - 1, y + radius - 1, argb);
   //--- Handle bottom-left: rounded arc or plain square corner
   if(rBL) FillCornerQuadrantHR(canvas, x + radius,     y + h - radius, radius, argb, -1,  1);
   else     canvas.FillRectangle(x, y + h - radius, x + radius - 1, y + h - 1, argb);
   //--- Handle bottom-right: rounded arc or plain square corner
   if(rBR) FillCornerQuadrantHR(canvas, x + w - radius, y + h - radius, radius, argb,  1,  1);
   else     canvas.FillRectangle(x + w - radius, y + h - radius, x + w - 1, y + h - 1, argb);
  }

//+------------------------------------------------------------------+
//| Fill a triangle via scanline rasterization at high resolution    |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillTriangleHR(CCanvas &canvas, int x0, int y0, int x1, int y1, int x2, int y2, uint argb)
  {
   //--- Store vertices as float arrays for scanline math
   double vx[3] = { (double)x0, (double)x1, (double)x2 };
   double vy[3] = { (double)y0, (double)y1, (double)y2 };
   //--- Find vertical extent of the triangle
   double minY = vy[0], maxY = vy[0];
   for(int i = 1; i < 3; i++) { if(vy[i] < minY) minY = vy[i]; if(vy[i] > maxY) maxY = vy[i]; }
   //--- Rasterize each horizontal scanline within the bounding range
   for(int scanY = (int)MathCeil(minY); scanY <= (int)MathFloor(maxY); scanY++)
     {
      //--- Sample at pixel center for accurate edge intersections
      double cy = (double)scanY + 0.5; double xi[6]; int nc = 0;
      //--- Test each edge for intersection with the current scanline
      for(int i = 0; i < 3; i++)
        {
         int ni = (i + 1) % 3;
         //--- Compute edge vertical extent
         double eMin = (vy[i] < vy[ni]) ? vy[i] : vy[ni], eMax = (vy[i] > vy[ni]) ? vy[i] : vy[ni];
         //--- Skip horizontal edges and scanlines outside this edge
         if(cy < eMin || cy > eMax || MathAbs(vy[ni] - vy[i]) < 1e-12) continue;
         //--- Compute intersection parameter t along the edge
         double t = (cy - vy[i]) / (vy[ni] - vy[i]);
         if(t < 0.0 || t > 1.0) continue;
         //--- Record horizontal intersection x-coordinate
         xi[nc++] = vx[i] + t * (vx[ni] - vx[i]);
        }
      //--- Sort intersection x-coordinates from left to right
      for(int a = 0; a < nc - 1; a++)
         for(int b = a + 1; b < nc; b++)
            if(xi[a] > xi[b]) { double tmp = xi[a]; xi[a] = xi[b]; xi[b] = tmp; }
      //--- Fill pixels between each pair of intersections
      for(int p = 0; p + 1 < nc; p += 2)
         for(int fx = (int)MathCeil(xi[p]); fx <= (int)MathFloor(xi[p + 1]); fx++)
            canvas.PixelSet(fx, scanY, argb);
     }
  }

//+------------------------------------------------------------------+
//| Fill a quadrilateral using scanline rasterization                |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillQuadrilateralBorder(CCanvas &canvas, double &vx[], double &vy[], uint argb)
  {
   //--- Find vertical extent of the quadrilateral
   double minY = vy[0], maxY = vy[0];
   for(int i = 1; i < 4; i++) { if(vy[i] < minY) minY = vy[i]; if(vy[i] > maxY) maxY = vy[i]; }
   //--- Rasterize each horizontal scanline within the bounding range
   for(int scanY = (int)MathCeil(minY); scanY <= (int)MathCeil(maxY) - 1; scanY++)
     {
      //--- Sample at pixel center for accurate edge intersections
      double cy = (double)scanY + 0.5; double xi[8]; int nc = 0;
      //--- Test each of the four edges for scanline intersection
      for(int i = 0; i < 4; i++)
        {
         int ni = (i + 1) % 4;
         //--- Compute edge vertical extent
         double eMin = (vy[i] < vy[ni]) ? vy[i] : vy[ni], eMax = (vy[i] > vy[ni]) ? vy[i] : vy[ni];
         //--- Skip horizontal edges and out-of-range scanlines
         if(cy < eMin || cy > eMax || MathAbs(vy[ni] - vy[i]) < 1e-12) continue;
         //--- Compute parameter t along this edge at the scanline
         double t = (cy - vy[i]) / (vy[ni] - vy[i]);
         if(t < 0.0 || t > 1.0) continue;
         //--- Record intersection x-coordinate
         xi[nc++] = vx[i] + t * (vx[ni] - vx[i]);
        }
      //--- Sort intersection x-coordinates from left to right
      for(int a = 0; a < nc - 1; a++)
         for(int b = a + 1; b < nc; b++)
            if(xi[a] > xi[b]) { double tmp = xi[a]; xi[a] = xi[b]; xi[b] = tmp; }
      //--- Fill pixels between each intersection pair
      for(int p = 0; p + 1 < nc; p += 2)
         for(int fx = (int)MathCeil(xi[p]); fx <= (int)MathCeil(xi[p + 1]) - 1; fx++)
            canvas.PixelSet(fx, scanY, argb);
     }
  }

//+------------------------------------------------------------------+
//| Draw a thick border edge between two endpoints                   |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DrawBorderEdge(CCanvas &canvas, double x0, double y0, double x1, double y1, int thickness, uint argb)
  {
   //--- Compute direction vector and segment length
   double dx = x1 - x0, dy = y1 - y0, len = MathSqrt(dx * dx + dy * dy);
   //--- Abort on degenerate zero-length segment
   if(len < 1e-6) return;
   //--- Compute perpendicular and tangent unit vectors
   double px = -dy / len, py = dx / len, ex = dx / len, ey = dy / len;
   //--- Compute half-thickness and slight cap extension for clean joins
   double ht = thickness / 2.0, ext = 0.23 * thickness;
   //--- Extend segment endpoints slightly for miter join coverage
   double sx = x0 - ex * ext, sy = y0 - ey * ext, ex2 = x1 + ex * ext, ey2 = y1 + ey * ext;
   //--- Build the four corners of the thick-edge quadrilateral
   double tvx[4] = { sx - px*ht, sx + px*ht, ex2 + px*ht, ex2 - px*ht };
   double tvy[4] = { sy - py*ht, sy + py*ht, ey2 + py*ht, ey2 - py*ht };
   //--- Fill the quadrilateral using scanline rasterization
   FillQuadrilateralBorder(canvas, tvx, tvy, argb);
  }

//+------------------------------------------------------------------+
//| Test whether an angle falls within a start-to-end arc range      |
//+------------------------------------------------------------------+
bool CCanvasPrimitives::IsAngleBetween(double angle, double start, double end)
  {
   //--- Normalize all angles to the [0, 2π) range
   double tp = 2.0 * M_PI;
   angle = MathMod(angle + tp, tp); start = MathMod(start + tp, tp); end = MathMod(end + tp, tp);
   //--- Return true if angle falls within the arc sweep from start to end
   return MathMod(angle - start + tp, tp) <= MathMod(end - start + tp, tp);
  }

//+------------------------------------------------------------------+
//| Draw an anti-aliased corner arc with specified thickness         |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DrawCornerArc(CCanvas &canvas, int cx, int cy, int radius, int thickness, uint argb, double startAngle, double endAngle)
  {
   //--- Compute outer and inner radii of the arc annulus
   double oR = (double)radius, iR = MathMax(0.0, (double)radius - thickness);
   //--- Extract base alpha and RGB from the color
   uchar  bA = (uchar)((argb >> 24) & 0xFF); uint rgb = argb & 0x00FFFFFF;
   //--- Set up 4x4 subpixel AA grid
   int sub = 4; double step = 1.0 / sub; int subSq = sub * sub, pr = (int)(oR + 2.0);
   //--- Iterate over the bounding square around the arc
   for(int dy = -pr; dy <= pr; dy++)
      for(int dx = -pr; dx <= pr; dx++)
        {
         //--- Compute radial distance from center
         double dist = MathSqrt((double)(dx * dx + dy * dy));
         //--- Skip pixels outside the annulus radial range
         if(dist > oR + 1.0 || dist < iR - 1.0) continue;
         //--- Skip pixels outside the angular sweep
         if(!IsAngleBetween(MathArctan2((double)dy, (double)dx), startAngle, endAngle)) continue;
         //--- Write fully covered interior pixels directly
         if(dist <= oR - 1.0 && dist >= iR + 1.0) { canvas.PixelSet(cx + dx, cy + dy, argb); continue; }
         //--- Count subpixel samples inside the arc annulus
         int inside = 0;
         for(int sy = 0; sy < sub; sy++)
            for(int sx = 0; sx < sub; sx++)
              {
               //--- Compute subpixel offset from pixel corner
               double sdx = (double)dx - 0.5 + (sx + 0.5) * step, sdy = (double)dy - 0.5 + (sy + 0.5) * step;
               double sd = MathSqrt(sdx * sdx + sdy * sdy);
               //--- Count subpixel if inside the annulus and within the arc angle
               if(sd >= iR && sd <= oR && IsAngleBetween(MathArctan2(sdy, sdx), startAngle, endAngle)) inside++;
              }
         //--- Skip pixels with zero subpixel coverage
         if(inside == 0) continue;
         //--- Write fully covered boundary pixels directly
         if(inside >= subSq) canvas.PixelSet(cx + dx, cy + dy, argb);
         //--- Blend AA boundary pixels with proportional alpha
         else BlendPixelSet(canvas, cx + dx, cy + dy, (((uint)(uchar)((int)bA * inside / subSq)) << 24) | rgb);
        }
  }

//+------------------------------------------------------------------+
//| Draw rounded rect border with per-corner rounding at high res    |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DrawSelectiveRoundRectBorderHR(CCanvas &canvas, int x, int y, int w, int h, int radius, uint argb, int thickness,
                                                        bool rTL, bool rTR, bool rBL, bool rBR)
  {
   //--- Skip drawing entirely when border width is disabled
   if(BorderWidth <= 0) return;
   //--- Clamp radius to half the shorter dimension
   radius = MathMin(radius, MathMin(w / 2, h / 2));
   //--- Cache per-corner radii and half-thickness for edge offsets
   int tlR = rTL ? radius : 0, trR = rTR ? radius : 0, blR = rBL ? radius : 0, brR = rBR ? radius : 0, h2 = thickness / 2;
   //--- Draw top edge between the two top corner tangent points
   DrawBorderEdge(canvas, x + tlR,     y + h2,     x + w - trR, y + h2,     thickness, argb);
   //--- Draw right edge only when at least one right corner is rounded
   if(rTR || rBR) DrawBorderEdge(canvas, x + w - h2, y + trR, x + w - h2, y + h - brR, thickness, argb);
   //--- Draw bottom edge between the two bottom corner tangent points
   DrawBorderEdge(canvas, x + w - brR, y + h - h2, x + blR,     y + h - h2, thickness, argb);
   //--- Draw left edge only when at least one left corner is rounded
   if(rTL || rBL) DrawBorderEdge(canvas, x + h2, y + h - blR, x + h2, y + tlR, thickness, argb);
   //--- Draw corner arcs for each enabled rounded corner
   if(rTL) DrawCornerArc(canvas, x + radius,     y + radius,     radius, thickness, argb, M_PI,       M_PI * 1.5);
   if(rTR) DrawCornerArc(canvas, x + w - radius, y + radius,     radius, thickness, argb, M_PI * 1.5, M_PI * 2.0);
   if(rBL) DrawCornerArc(canvas, x + radius,     y + h - radius, radius, thickness, argb, M_PI * 0.5, M_PI);
   if(rBR) DrawCornerArc(canvas, x + w - radius, y + h - radius, radius, thickness, argb, 0.0,        M_PI * 0.5);
  }

//+------------------------------------------------------------------+
//| Draw a smooth anti-aliased line using Wu's algorithm             |
//+------------------------------------------------------------------+
//--- thickness == 1: Wu anti-aliased 1-pixel line. Named "Bresenham"
//--- internally for historical reasons; the implementation is Wu's
//--- algorithm (fractional-part intensity weighting), not Bresenham's.
void CCanvasPrimitives::DrawBresenhamLine(CCanvas &canvas, int x0, int y0, int x1, int y1, uint argb)
  {
   //--- Extract base alpha and RGB from the color
   uchar bA = (uchar)((argb >> 24) & 0xFF);
   uint  rgb = argb & 0x00FFFFFF;
   //--- Cache canvas dimensions for bounds checks inside the macro
   int   cW  = canvas.Width(), cH = canvas.Height();

   //--- Define inline pixel-plot macro with bounds check and alpha scale
   #define WU_PLOT(px, py, brightness) \
   { \
      int _x = (px), _y = (py); \
      if(_x >= 0 && _x < cW && _y >= 0 && _y < cH) \
        { \
         uchar _a = (uchar)((double)bA * (brightness)); \
         if(_a > 0) BlendPixelSet(canvas, _x, _y, (((uint)_a) << 24) | rgb); \
        } \
   }

   //--- Swap axes for steep lines to always iterate along the major axis
   bool steep = MathAbs(y1 - y0) > MathAbs(x1 - x0);
   if(steep)   { int t=x0;x0=y0;y0=t; t=x1;x1=y1;y1=t; }
   //--- Ensure iteration always goes left to right
   if(x0 > x1) { int t=x0;x0=x1;x1=t; t=y0;y0=y1;y1=t; }

   //--- Compute slope gradient
   double dx   = x1 - x0;
   double dy   = y1 - y0;
   double grad = (dx == 0.0) ? 1.0 : dy / dx;

   //--- Process the first endpoint with fractional coverage
   double xend  = MathRound((double)x0);
   double yend  = y0 + grad * (xend - x0);
   double xgap  = 1.0 - MathMod((double)x0 + 0.5, 1.0);
   int    xpxl1 = (int)xend;
   int    ypxl1 = (int)yend;
   double fpart = MathMod(yend, 1.0);
   double rfpart= 1.0 - fpart;
   //--- Plot first endpoint pixels for steep and shallow cases
   if(steep) { WU_PLOT(ypxl1,   xpxl1, rfpart * xgap); WU_PLOT(ypxl1+1, xpxl1, fpart  * xgap); }
   else      { WU_PLOT(xpxl1, ypxl1,   rfpart * xgap); WU_PLOT(xpxl1, ypxl1+1, fpart  * xgap); }
   //--- Initialize interpolated y for the main loop
   double intery = yend + grad;

   //--- Process the second endpoint with fractional coverage
   xend  = MathRound((double)x1);
   yend  = y1 + grad * (xend - x1);
   xgap  = MathMod((double)x1 + 0.5, 1.0);
   int xpxl2 = (int)xend;
   int ypxl2 = (int)yend;
   fpart  = MathMod(yend, 1.0);
   rfpart = 1.0 - fpart;
   //--- Plot second endpoint pixels for steep and shallow cases
   if(steep) { WU_PLOT(ypxl2,   xpxl2, rfpart * xgap); WU_PLOT(ypxl2+1, xpxl2, fpart  * xgap); }
   else      { WU_PLOT(xpxl2, ypxl2,   rfpart * xgap); WU_PLOT(xpxl2, ypxl2+1, fpart  * xgap); }

   //--- Plot interior pixels along the major axis
   if(steep)
     {
      for(int x = xpxl1+1; x <= xpxl2-1; x++)
        {
         //--- Compute fractional and complement coverage at this position
         fpart  = MathMod(intery, 1.0);
         rfpart = 1.0 - fpart;
         //--- Plot upper and lower AA pixel pair
         WU_PLOT((int)intery,   x, rfpart);
         WU_PLOT((int)intery+1, x, fpart);
         intery += grad;
        }
     }
   else
     {
      for(int x = xpxl1+1; x <= xpxl2-1; x++)
        {
         //--- Compute fractional and complement coverage at this position
         fpart  = MathMod(intery, 1.0);
         rfpart = 1.0 - fpart;
         //--- Plot upper and lower AA pixel pair
         WU_PLOT(x, (int)intery,   rfpart);
         WU_PLOT(x, (int)intery+1, fpart);
         intery += grad;
        }
     }
   //--- Undefine the plot macro to avoid polluting the global scope
   #undef WU_PLOT
  }

//+------------------------------------------------------------------+
//| Draw a thick line with AA edges between two points               |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DrawThickLine(CCanvas &canvas, int x0, int y0, int x1, int y1, int thickness, uint argb)
  {
   //--- Delegate to single-pixel Wu line for thickness of 1
   if(thickness <= 1)
     {
      DrawBresenhamLine(canvas, x0, y0, x1, y1, argb);
      return;
     }

   //--- Compute direction vector components and total segment length
   double dx = (double)(x1 - x0);
   double dy = (double)(y1 - y0);
   double len = MathSqrt(dx*dx + dy*dy);
   //--- Fall back to 1-pixel line for degenerate zero-length segment
   if(len < 1e-6)
     {
      DrawBresenhamLine(canvas, x0, y0, x1, y1, argb);
      return;
     }
   //--- Compute tangent unit vector along the segment
   double ux = dx / len;
   double uy = dy / len;
   //--- Compute perpendicular unit vector for stroke width
   double px = -uy;
   double py =  ux;

   //--- Compute half-thickness and sub-pixel centering shift for even widths
   double halfT = thickness / 2.0;
   double halfPxShift = ((thickness & 1) == 0) ? 0.5 : 0.0;
   double cx = -px * halfPxShift;
   double cy = -py * halfPxShift;

   //--- Build pixel bounding box with padding for AA coverage
   int minX = MathMin(x0, x1) - (int)MathCeil(halfT) - 1;
   int maxX = MathMax(x0, x1) + (int)MathCeil(halfT) + 1;
   int minY = MathMin(y0, y1) - (int)MathCeil(halfT) - 1;
   int maxY = MathMax(y0, y1) + (int)MathCeil(halfT) + 1;
   //--- Clamp bounding box to canvas dimensions
   int cW = canvas.Width(), cH = canvas.Height();
   if(minX < 0) minX = 0;
   if(minY < 0) minY = 0;
   if(maxX >= cW) maxX = cW - 1;
   if(maxY >= cH) maxY = cH - 1;

   //--- Extract base alpha and RGB from the input color
   uchar bA = (uchar)((argb >> 24) & 0xFF);
   uint  rgb = argb & 0x00FFFFFF;

   //--- Set up 4x4 SSAA grid constants
   const int    SS       = 4;
   double subStep  = 1.0 / SS;
   double subStart = -0.5 + subStep * 0.5;
   int    subCount = SS * SS;

   //--- Iterate over every pixel in the bounding box
   for(int sy = minY; sy <= maxY; sy++)
     {
      for(int sx = minX; sx <= maxX; sx++)
        {
         //--- Count subpixel samples inside the rotated stroke rectangle
         int inside = 0;
         for(int vy = 0; vy < SS; vy++)
           {
            for(int vx = 0; vx < SS; vx++)
              {
               //--- Compute subpixel world position
               double subX = (double)sx + subStart + subStep * vx;
               double subY = (double)sy + subStart + subStep * vy;
               //--- Project subpixel relative to stroke start with centering offset
               double qx = subX - (double)x0 - cx;
               double qy = subY - (double)y0 - cy;
               //--- Compute along-segment and perpendicular distances
               double along = qx * ux + qy * uy;
               double perp  = qx * px + qy * py;
               //--- Count if inside the stroke rectangle bounds
               if(along >= 0.0 && along <= len &&
                  perp >= -halfT && perp <= halfT)
                  inside++;
              }
           }
         //--- Skip pixels with no subpixel coverage
         if(inside == 0) continue;
         //--- Compute coverage-weighted alpha
         uchar cov = (uchar)((int)bA * inside / subCount);
         if(cov == 0) continue;
         //--- Blend pixel with coverage alpha
         BlendPixelSet(canvas, sx, sy, (((uint)cov) << 24) | rgb);
        }
     }
  }

//+------------------------------------------------------------------+
//| Fill a circle with anti-aliased edge                             |
//+------------------------------------------------------------------+
void CCanvasPrimitives::FillCircleAA(CCanvas &canvas, int cx, int cy, int radius, uint argb)
  {
   //--- Cache radius as float and extract base alpha and RGB
   double rd = (double)radius;
   uchar  bA = (uchar)((argb >> 24) & 0xFF);
   uint   rgb = argb & 0x00FFFFFF;
   //--- Set up 4x4 subpixel AA grid
   int sub = 4; double step = 1.0 / sub; int subSq = sub * sub;
   //--- Iterate over bounding box with one-pixel AA margin
   for(int dy = -radius - 1; dy <= radius + 1; dy++)
      for(int dx = -radius - 1; dx <= radius + 1; dx++)
        {
         //--- Compute radial distance from circle center
         double dist = MathSqrt((double)(dx * dx + dy * dy));
         //--- Skip pixels fully outside the circle
         if(dist > rd + 1.0) continue;
         //--- Write fully covered interior pixels directly
         if(dist <= rd - 1.0) { canvas.PixelSet(cx + dx, cy + dy, argb); continue; }
         //--- Count subpixel samples inside the circle radius
         int inside = 0;
         for(int sy = 0; sy < sub; sy++)
            for(int sx = 0; sx < sub; sx++)
              {
               //--- Compute subpixel offset from pixel corner
               double sdx = (double)dx - 0.5 + (sx + 0.5) * step;
               double sdy = (double)dy - 0.5 + (sy + 0.5) * step;
               if(sdx * sdx + sdy * sdy <= rd * rd) inside++;
              }
         //--- Skip pixels with zero subpixel coverage
         if(inside == 0) continue;
         //--- Blend AA boundary pixel with proportional alpha
         BlendPixelSet(canvas, cx + dx, cy + dy, (((uint)(uchar)((int)bA * inside / subSq)) << 24) | rgb);
        }
  }

//+------------------------------------------------------------------+
//| Draw a circle border with anti-aliasing                          |
//+------------------------------------------------------------------+
void CCanvasPrimitives::DrawCircleBorderAA(CCanvas &canvas, int cx, int cy, int radius, int thickness, uint argb)
  {
   //--- Delegate to DrawCornerArc using a full 360-degree sweep
   DrawCornerArc(canvas, cx, cy, radius, thickness, argb, 0.0, M_PI * 2.0);
  }

//+------------------------------------------------------------------+
//| CLASS 2 — Theme manager: apply and toggle dark/light color sets  |
//+------------------------------------------------------------------+
class CThemeManager : public CCanvasPrimitives
  {
protected:
   bool          m_isDarkTheme;  // Current theme state: true = dark, false = light
   ThemeColorSet m_themeColors;  // Active color set for all UI components

protected:
   void ApplyTheme();  // Load colors matching the current theme state
   void ToggleTheme(); // Flip theme and reload colors
  };

//+------------------------------------------------------------------+
//| Load color values matching the current theme state               |
//+------------------------------------------------------------------+
void CThemeManager::ApplyTheme()
  {
   //--- Apply dark-theme color palette
   if(m_isDarkTheme)
     {
      m_themeColors.sidebarBackground         = C'30,34,45';
      m_themeColors.sidebarBorder             = C'200,210,225';
      m_themeColors.buttonHoverBackground     = C'30,100,200';
      m_themeColors.buttonActiveBackground    = C'41,98,255';
      m_themeColors.buttonIconColor           = C'220,225,235';
      m_themeColors.buttonIconActiveColor     = clrWhite;
      m_themeColors.flyoutBackground          = C'36,41,54';
      m_themeColors.flyoutBorder              = C'200,210,225';
      m_themeColors.flyoutItemHoverBackground = C'30,100,200';
      m_themeColors.flyoutTextColor           = C'200,210,225';
      m_themeColors.flyoutTextActiveColor     = clrWhite;
      m_themeColors.flyoutTitleColor          = C'90,105,130';
      m_themeColors.gripDotsColor             = C'90,100,120';
      m_themeColors.closeButtonHoverColor     = C'235,55,55';
      m_themeColors.themeButtonHoverColor     = C'255,200,50';
      m_themeColors.separatorColor            = C'44,50,64';
      m_themeColors.accentBarColor            = C'41,98,255';
      m_themeColors.scrollArrowColor          = C'120,130,150';
      m_themeColors.scrollArrowHoverColor     = clrWhite;
     }
   //--- Apply light-theme color palette
   else
     {
      m_themeColors.sidebarBackground         = clrWhite;
      m_themeColors.sidebarBorder             = C'30,35,45';
      m_themeColors.buttonHoverBackground     = C'30,100,200';
      m_themeColors.buttonActiveBackground    = C'41,98,255';
      m_themeColors.buttonIconColor           = C'40,45,58';
      m_themeColors.buttonIconActiveColor     = clrWhite;
      m_themeColors.flyoutBackground          = clrWhite;
      m_themeColors.flyoutBorder              = C'30,35,45';
      m_themeColors.flyoutItemHoverBackground = C'30,100,200';
      m_themeColors.flyoutTextColor           = C'40,45,58';
      m_themeColors.flyoutTextActiveColor     = clrWhite;
      m_themeColors.flyoutTitleColor          = C'130,140,160';
      m_themeColors.gripDotsColor             = C'160,170,185';
      m_themeColors.closeButtonHoverColor     = C'210,35,35';
      m_themeColors.themeButtonHoverColor     = C'150,100,0';
      m_themeColors.separatorColor            = C'210,215,225';
      m_themeColors.accentBarColor            = C'41,98,255';
      m_themeColors.scrollArrowColor          = C'120,130,145';
      m_themeColors.scrollArrowHoverColor     = C'40,45,58';
     }
  }

//+------------------------------------------------------------------+
//| Toggle between dark and light theme and reapply colors           |
//+------------------------------------------------------------------+
void CThemeManager::ToggleTheme()
  {
   //--- Invert the current theme flag
   m_isDarkTheme = !m_isDarkTheme;
   //--- Reload the full color set for the new theme
   ApplyTheme();
  }

#endif // TOOLS_PALETTE_PRIMITIVES_MQH
//+------------------------------------------------------------------+