I'm creating a C++ wxWidgets calculator application. I have a wxTextCtrl that displays the current calculation. It is set to read-only because I'm using wxKeyEvent's to write in it. The problem is, that although it is set to read-only, the caret is still shown:
I want the text in the text control to be selectable and uneditable, without the caret.
Any suggestions?
Here's an example of how to create a read only styled text control that should look and behave in the way you're describing. wxStyledTextCtrl has many, many methods, so the look and behavior can be further customized if needed.
MainText = new wxStyledTextCtrl(<parent>, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxBORDER_NONE);
// Set a small minimimum size.
MainText->SetMinClientSize(wxSize(0,0));
// Set the default style to use the Lato bold font.
MainText->StyleSetFaceName(0, "Lato");
MainText->StyleSetBold(0, true);
// Set the control read only and set the caret invisible.
MainText->SetReadOnly(true);
MainText->SetCaretStyle(wxSTC_CARETSTYLE_INVISIBLE);
// Hode the horizontal scroll bar and the left margin.
MainText->SetUseHorizontalScrollBar(false);
MainText->SetMarginWidth(1,0);
// Use the newer D2D drawing.
MainText->SetTechnology(wxSTC_TECHNOLOGY_DIRECTWRITE);
I learned while working on this answer that there is an option to set the caret invisible, so it is unnecessary to set it to the background color like I mentioned above.
For sizing the font to fill most of the control, you can increase and decrease the zoom until text size is adequate. I think this should also fix the sizing problem from your other question.
MainText->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
int stcHt = evt.GetSize().GetHeight()/1.3;
int zoom = MainText->GetZoom();
if ( stcHt > MainText->TextHeight(0) )
{
// Increase the zoom until a line of text is taller than stcHt.
while ( stcHt > MainText->TextHeight(0) )
{
zoom = MainText->GetZoom();
MainText->SetZoom(zoom+1);
}
}
else
{
// Decrease the zoom until a line of text is shorter than stcHt.
while ( stcHt <= MainText->TextHeight(0) )
{
zoom = MainText->GetZoom();
MainText->SetZoom(zoom-1);
}
zoom = MainText->GetZoom();
}
MainText->SetZoom(zoom);
});
You said that you are updating the text in response to key events. To do that with the styled text control, you'll need to set the control to not be read only, make your changes, and then set it back to read only. Something like this:
MainText->SetReadOnly(false);
//update the text in response to the key event.
MainText->SetReadOnly(true);
There are several methods you can use for updating the text. I think the ones most likely to be useful are InsertText, AppendText, and AddText.
If you want to try to remove the extra padding between the text showing the calculation and the text showing the result, there is method SetExtraAscent
which can be passed a negative number to remove extra space above text characters in each line. The problem is that the input is a number of raw pixels. However the size event handler works by setting a zoom, so the number of pixels will be different for each zoom level.
There is a workaround though. The value returned by MainText->TextHeight
is basically the sum of the metrics ascent, descent, and internal leading that I mentioned here. To remove the extra padding at the top of each line, we can set SetExtraAscent
to basically be the negative of internal leading.
The calculations to do this are a little tricky. First we can approximate how much of a font's height is made up of the internal leading. The easiest way I can think of to do this is to create a temporary memory dc and get the metrics like so:
// Create a temporary memory dc to do some font calculations.
wxMemoryDC mem;
wxFont font(wxFontInfo(wxSize(0, 1000))
.Family(wxFONTFAMILY_SWISS)
.FaceName("Lato")
.Bold());
mem.SetFont(font);
wxFontMetrics metrics = mem.GetFontMetrics();
double internalLeadingPecent = static_cast<double>(metrics.internalLeading) /
static_cast<double>(metrics.height);
Then the size handler can be adjusted to try to remove the internal leading something like this:
MainText->Bind(wxEVT_SIZE, [this, internalLeadingPecent](wxSizeEvent& evt) {
evt.Skip();
// First set the extra accent back to zero so that we get
MainText->SetExtraAscent(0);
int stcHt = evt.GetSize().GetHeight();
int zoom = MainText->GetZoom();
int internalLead = internalLeadingPecent*stcHt;
if ( stcHt > MainText->TextHeight(0) - internalLead )
{
// Increase the zoom until a line of text is taller than stcHt.
while ( stcHt > MainText->TextHeight(0) - internalLead)
{
zoom = MainText->GetZoom();
MainText->SetZoom(zoom+1);
}
}
else
{
// Decrease the zoom until a line of text is shorter than stcHt.
while ( stcHt <= MainText->TextHeight(0) - internalLead )
{
zoom = MainText->GetZoom();
MainText->SetZoom(zoom-1);
}
zoom = MainText->GetZoom();
}
MainText->SetExtraAscent(-1*internalLead);
MainText->SetZoom(zoom);
});
My quick experiments show this removes most of the padding between a styled text control and the window above it. If you need to remove more padding, you can try to adjust things further like removing '-1.2*internalLead' if that seem to work better. But those adjustments can only be found by experimenting. Fonts are really hard to deal with.