Search code examples
delphicanvasgraphicsc++buildervcl

How to change a color in an area of a canvas


I'm trying to click and drag on a TCanvas and change the background color to clHighlight as I drag down and change it back to clWhite if I were to reverse the drag direction. Pretty much what would happen were you to click on this text you are reading and drag down or up, same idea anyhow. I've gleaned what I could from this link

and I've edited the code below extensively since I first posted this, the code below now does what I set out to do except that the text in the selection can get messed up if you don't drag at an even pace, which is unacceptable. If you really move the mouse up and down quickly it will definitely get messed up. If you move the mouse at a steady pace it works well which makes me fear there may not be a solution to this problem. I'm continuing to look at that, if anyone has any suggestions that'd be great. I've also gotten a couple "Out of resources" errors, not sure what's up with that yet, I'm freeing the Bitmaps, no other resources are involved that I'm aware of.

The following code is in a MouseMove event.srect is the selected rectangle in the TImage's Canvas. drect is the same srect translated to 0,0. bm1 contains the selected rect copied from the TImage's Canvas using CopyRect. bm2 contains bm1 with the clWhite (background) changed to clHighlight (or the reverse) using BrushCopy. BitBlt is then used to copy bm2 back into the original TImage's selected rectangle.

    // vp is derived from TImage
    if (Y > sel_data->_last_y)
    {
      TRect srect = Rect(sel_data->_rect.Left,sel_data->_last_y,sel_data->_rect.Right, Y);
      TRect drect = Rect(sel_data->_rect.Left,0,sel_data->_rect.Right, sel_data->_rect.Height() - 1);
      Graphics::TBitmap* bm1 = new Graphics::TBitmap;
      bm1->Width = srect.Width();
      bm1->Height = srect.Height();
      Graphics::TBitmap* bm2 = new Graphics::TBitmap;
      bm2->Width = srect.Width();
      bm2->Height = srect.Height();

      bm1->Canvas->CopyRect(drect, vp->Canvas, srect);

      bm2->Canvas->Brush->Color = clHighlight;
      bm2->Canvas->BrushCopy(drect, bm1, drect, clWindow);

      BitBlt(vp->Canvas->Handle, srect.Left, srect.Top, srect.Width(), srect.Height(),
         bm2->Canvas->Handle, 0, 0, SRCCOPY);  
      vp->Refresh();
      delete bm1;
      delete bm2;
    }
    else if (Y < sel_data->_last_y)
    {
      TRect srect = Rect(sel_data->_rect.Left, Y,sel_data->_rect.Right, sel_data->_last_y);
      TRect drect = Rect(sel_data->_rect.Left,0,sel_data->_rect.Right, sel_data->_rect.Height() - 1);
      Graphics::TBitmap* bm1 = new Graphics::TBitmap;
      bm1->Width = srect.Width();
      bm1->Height = srect.Height();
      Graphics::TBitmap* bm2 = new Graphics::TBitmap;
      bm2->Width = srect.Width();
      bm2->Height = srect.Height();

      bm1->Canvas->CopyRect(drect, vp->Canvas, srect);

      bm2->Canvas->Brush->Color = clWhite;
      bm2->Canvas->BrushCopy(drect, bm1, drect, clHighlight);

      int w = srect.Width();
      int h = srect.Height();
      BitBlt(vp->Canvas->Handle, srect.Left, srect.Top, w, h, bm2->Canvas->Handle, 0, 0, SRCCOPY);  
      vp->Refresh();
      delete bm1;
      delete bm2;
    }
    sel_data->_last_y = Y;
  }

Solution

  • This is what I ended up with, all the glitches have been fixed as far as I know, I'm including the entire MouseMove event handler. The above mentioned link clued me in that I needed a CopyRect, a BrushCopy, and then a BitBlt.

    void __fastcall Tviewer_ui::viewerPageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
    {
      if (_selecting)
      {
        TColor to_color = 0;
        TColor from_color = 0; 
        Tviewer_page* vp = static_cast<Tviewer_page *>(Sender);
        Tsel_data* sel_data = &vp->_sel_datas[vp->_c_sel];
    
        // Scroll window if needed
        int sb_pos = _scrollBox->VertScrollBar->Position;
        if (Y > sb_pos - vp->_top + _scrollBox->Height - 10)
          _scrollBox->VertScrollBar->Position += 10;
        else 
          if (Y < sb_pos - vp->_top + 10)
            _scrollBox->VertScrollBar->Position -= 10;
    
        int y1 = 0;
        int y2 = 0;
        if (Y > sel_data->_start_y)
        {
          if (Y > sel_data->_last_y)
          {
            y1 = sel_data->_last_y;
            y2 = Y;
            from_color = clWhite;
            to_color = clHighlight;
          }
          else if (Y < sel_data->_last_y)
          {
            y1 = Y;
            y2 = sel_data->_last_y;
            from_color = clHighlight;
            to_color = clWhite;
          }
          sel_data->_rect.Bottom = Y;
        }
        else if (Y < sel_data->_start_y)
        {
          if (Y < sel_data->_last_y)
          {
            y1 = Y;
            y2 = sel_data->_last_y;
            from_color = clWhite;
            to_color = clHighlight;
          }
          else if (Y > sel_data->_last_y)
          {
            y1 = sel_data->_last_y;
            y2 = Y;
            from_color = clHighlight;
            to_color = clWhite;
          }
          sel_data->_rect.Top = Y;
        }
        int height = abs(y1 - y2);
        if (height > 0)
        {
          TRect srect = Rect(sel_data->_rect.Left, y1, sel_data->_rect.Right, y2);
          int width = srect.Width();
          TRect drect = Rect(sel_data->_rect.Left,0,sel_data->_rect.Right, height);
          Graphics::TBitmap* bm1 = new Graphics::TBitmap;
          bm1->Width = width;
          bm1->Height = height;
          Graphics::TBitmap* bm2 = new Graphics::TBitmap;
          bm2->Width = width;
          bm2->Height = height;
    
          bm1->Canvas->CopyRect(drect, vp->Canvas, srect);
          bm2->Canvas->Brush->Color = to_color;
          bm2->Canvas->BrushCopy(drect, bm1, drect, from_color);
    
          BitBlt(vp->Canvas->Handle, srect.Left, srect.Top, width, height, bm2->Canvas->Handle, 0, 0, SRCCOPY);  
          vp->Refresh();
          delete bm1;
          delete bm2;
        } 
        sel_data->_last_y = Y;
      }
    }