Question

I'm trying to elaborate a TextCtrl with some line drawings: simple stuff I want to draw on the text, next to it, between lines, etc. And I'm approaching drawing in Wx like I've done it before, on WxPanels: with DCs: $dc->DrawLine( 15, 15, 120, 120); etc.

So far, I'm doing this: in a class derived from TextCtrl, I catch EVT_PAINT and implement my own OnPaint function:

sub new {
     my $class  = shift;
     my $parent = shift;

     my $self = $class->SUPER::new( $parent, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

     EVT_PAINT($self, \&OnPaint );

     return $self;
}

sub OnPaint {
    print"OnPaint: @_ \n";

    # @ravenspoint: this is there I would call the base's paint method
    # first, right? But how??

    my $linesCnt = $_[0]->GetNumberOfLines();

        my $dc = Wx::PaintDC->new($_[0]);
        $dc->DrawLine(
            15, 15,
            120, 120
        );

    # commented out as the base class' paint method will follow,
    # erasing what we've just drawn
    #   $_[1]->Skip(1);
};

Now, the thing is: either the line or the text in the control is drawn, when I let the paint event propagate up or not. For any Wx guru, this might be obvious, but for me, it's not. As I understand a TextCtrl, it is an agglomerate of a WxWindow, and a low level way of drawing text into this, with a dc, and DrawText() and some convenience functions on top, to set the caret etc.

This understanding means for me: to be able to draw, I would need access to the already prepared/drawn-upon dc so I can get on drawing onto it. Right? Is that possible, in general, and in WxPerl? In my function above, I'm doing my paint before the control draws the text, that's what I think is the cause for the line disappearing when I uncomment Skip() - as the control then clears and redraws the dc.

Is there a way to get into the loop after the TextCtrl has done all it's drawing?

P.S.

Plain TextCtrl seems to be implemented by calling a native text entry on most platforms, although the docs don't mention that.

I dug around in Kephra and Padre, which both show a "right margin indicator", a thin vertical line usually grey, drawn onto the text area. How did they do it? Surprise: they subclass Wx::Scintilla or the older Wx::StyledTextCtrl which both offer the SetEdgeColumn() method to trigger that.

Asking myself if reimplementing my own text widget is the only way to get access/hook into the drawing of text onto the DC. (Shaking head in disbelief)

Tinkering with *EVT_ERASE_BACKGROUND* and RichtTextCtrl's PaintBackground() without success (well, after a few minutes of hacking).

Was it helpful?

Solution

This is not an answer you want to hear, but you can't draw on a native control (and wxTextCtrl is one such). It draws itself already and you can't interfere with its drawing logic, especially considering that it's different under different platforms. So while you can make it work sometimes (especially under MSW where just anything is possible, i.e. you're completely free to shoot yourself in the foot in many different ways), this is not guaranteed to work under all platforms and you really shouldn't do it at all.

If you want some advanced capabilities not provided by wxTextCtrl, you should probably look at wxRichTextCtrl instead. It is also implemented entirely in wxWidgets itself, i.e. is not native, and so you can draw over it.

OTHER TIPS

In your function that overrides the base class paint method, call the base class paint method before your own code.

For what it is worth, here is how to do it with C++

wxTextCtrl::OnPaint()

I am trying to act on VZ's advice. Below is a minimal program intended to create a RichTextCtrl and draw a line across it. Instead it only draws a RichTextCtrl box. If I comment out the line beginning with "self.rtc = ", it instead draws only the line, not the RichTextContrl.

# sources:
  # http://wiki.wxpython.org/VerySimpleDrawing
  # https://github.com/wxWidgets/wxPython/blob/master/demo/RichTextCtrl.py

import wx
import wx.richtext as rt

class DrawPanel(wx.Frame):
    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)
        self.rtc = rt.RichTextCtrl(self,style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, event=None):
        dc = wx.PaintDC(self)
        dc.Clear()
        dc.SetPen(wx.Pen(wx.BLACK, 2))
        dc.DrawLine(0, 0, 50, 50)

app = wx.App(False)
frame = DrawPanel(None)
frame.Show()
app.MainLoop()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top