I've been thinking all this while how to check if caret is visible on the present Screen view of a Richtextbox.
The issue is that the caret may be at the end of the richtextbox and the scroll is at the beginning of the box, how can I programmatically know if the caret is visible on the present screen view or not.
NB. the caret may not necessarily be at the bottom.
Assuming this is WinForms
One thing that I think you could use is RichTextBox1.GetPositionFromCharIndex(), and RichTextBox1.SelectionStart:
' Y pos of caret
Dim CaretYPos As Integer = RichTextBox1.GetPositionFromCharIndex(RichTextBox1.SelectionStart).Y
Dim CharHeight As Integer = 4 ' Height of each line
If CaretYPos >= RichTextBox1.Height - CharHeight Then
' Caret is hidden below screen view
ElseIf CaretYPos < -CharHeight Then
' Caret is hidden above screen view
Else
' Caret is visible
End If
Though you might have to account for higher dpi displays when it comes to the CharHeight
I adapted #jimi 's comment to my C# application and it worked fine in conjunction with the ScrollToCaret() method. Thanks.
private void CheckAndMakeSelectionVisible()
{
// other stuff
if(!IsCaretVisible()) {
richTextMsg.ScrollToCaret();
}
}
private bool IsCaretVisible()
{
if (!richTextMsg.ClientRectangle.Contains(richTextMsg.GetPositionFromCharIndex(richTextMsg.SelectionStart)))
{
// Outside visible bounds
return false;
}
else
return true;
}
Related
How do i make so that positions adapts to the new window position when i resize my window in SDL2 and with SDL_RenderSetLogicalSize?
I want to be able to hover a text and make it change color but whenever i resize the window its still in the same window cords. Is there a way to adapt the mouse?
void MainMenu::CheckHover()
{
for (std::list<MenuItem>::iterator it = menuItems.begin(); it != menuItems.end(); it++)
{
Text* text = (*it).text;
float Left = text->GetX();
float Right = text->GetX() + text->GetWidth();
float Top = text->GetY();
float Bottom = text->GetY() + text->GetHeight();
if (mouseX < Left ||
mouseX > Right ||
mouseY < Top ||
mouseY > Bottom)
{
//hover = false
text->SetTextColor(255, 255, 255);
}
else
{
//hover = true
text->SetTextColor(100, 100, 100);
}
}
}
I had a similar problem some time ago, and it was due to multiple updates of my mouse position in one SDL eventloop. I wanted to move a SDL_Texture around by dragging with the mouse but it failed after resizing, because somehow the mouse coordinates were messed up.
What I did was rearrange my code to have only one event handling the mouse position update. Also I'm not using any calls to SDL_SetWindowSize(). When the user resizes the window the renderer is resized appropriately due to SDL_RenderSetLogicalSize().
The relevant code parts look like this - some stuff is adapted to your case. I would also suggest to use a SDL_Rect to detect if the mouse is inside your text area, because the SDL_Rects will be resized internally if the the window/renderer changes size.
//Declarations
//...
SDL_Point mousePosRunning;
// Picture in picture texture I wanted to move
SDL_Rect pipRect;
// Init resizable sdl window
window = SDL_CreateWindow(
"Window",
SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),
SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),
defaultW, defaultH,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE );
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // This one is optional
SDL_RenderSetLogicalSize(renderer, defaultW, defaultH);
// SDL main loop
while(SDL_PollEvent(&event) && running)
{
switch (event.type)
{
// Some event handling here
// ...
// Handle mouse motion event
case SDL_MOUSEMOTION:
// Update mouse pos
mousePosRunning.x = event.button.x;
mousePosRunning.y = event.button.y;
// Check if mouse is inside the pip region
if (SDL_EnclosePoints(&mousePosRunning, 1, &pipRect, NULL))
{
// Mouse is inside the pipRect
// do some stuff... i.e. change color
}
else
{
// Mouse left rectangle
}
break;
}
}
I have Rad Mask TextBox and a button. So user can enter any case into the text box. when button is clicked, I am fetching some record based on text and event I have to change the text to upper, trim and position the caret to end.
I noticed, if upper case is entered, I get all the scenario's but when lower case is entered the postion of cursor is pointed to beginning.
this is what I tried.
txtSearch.MaskedText = txtSearch.MaskedText.ToUpperInvariant().Trim();
txtSearch.SelectionOnFocus = SelectionOnFocus.CaretToEnd;
I really appreciate your help.
SelectionStart has solved the requirement.
Dispatcher.BeginInvoke(() =>
{
if (txtSearch.MaskedText != null)
{
txtSearch.MaskedText = txtSearch.MaskedText.ToUpper();
txtSearch.SelectionStart = txtSearch.MaskedText.Length;
txtSearch.Focus();
}
});
I'm trying to elaborate if the complete form is visible on screen. To clarify this: I don't care if the form is partially or fully hidden by another form, I just want to know, if the form is completely on the screen.
In Windows it is possible to move forms around, such that they are hidden half way. That is because you can move them past the actual bounds of any monitor. (Further to the left, right or bottom.) How can I check if that is the case in an easy way?
What I figured I could do is to check if the form is in bounds of SystemInformation.VirtualScreen. The problem here is, that not every pixel of the virtual screen is actually visible. Of course this would work if SystemInformation.MonitorCount = 1
Still I'm not really happy with this.
Public Function IsOnScreen(ByVal form As Form) As Boolean
Dim screens() As Screen = Screen.AllScreens
For Each scrn As Screen In screens
Dim formRectangle As Rectangle = New Rectangle(form.Left, form.Top, form.Width, form.Height)
If scrn.WorkingArea.Contains(formRectangle) Then
Return True
End If
Next
Return False
End Function
Best way I can think of is that you check that all four corners of the form are on a screen. Like this:
public bool FormOnScreen(Form frm) {
if (frm.IsHandleCreated) throw new InvalidOperationException();
if (!frm.Visible || frm.WindowState == FormWindowState.Minimized) return false;
return PointVisible(new Point(frm.Left, frm.Top)) &&
PointVisible(new Point(frm.Right, frm.Top)) &&
PointVisible(new Point(frm.Right, frm.Bottom)) &&
PointVisible(new Point(frm.Left, frm.Bottom));
}
private static bool PointVisible(Point p) {
var scr = Screen.FromPoint(p);
return scr.Bounds.Contains(p);
}
I need to use a RichTextBox, not a normal textbox because of the way it keeps the caret position, from line to line. But I need to keep the text in the same font all the time even if it is pasted.
At the moment I have it selecting the entire text and changing the font to the original (Lucida Console) but it look horrible when you paste into it as it flashes blue.
If you are handling the pasting programatically don't use the Paste method. Instead use Clipboard.GetDataObject().GetData(DataFormats.Text) to get the text in a string and then add the text using the Rtf or Text property to the RichTextBox:
string s = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
richTextBox.Text += s;
Otherwise you could handle the Ctrl+V key press:
void RichTextBox1_KeyDown(object sender, KeyEventArgs e)
{
if(e.Control == true && e.KeyCode == Keys.V)
{
string s = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
richTextBox.Text += s;
e.Handled = true; // disable Ctrl+V
}
}
Darin's method ignores caret position and always appends to the end of text.
Actually, there are better method. Use overload of RichTextBox.Paste():
DataFormats.Format plaintext_format = DataFormats.GetFormat(DataFormats.Text);
this.Paste(plaintext_format);
Works like charm for me.
Both #Darin & #idn's answers are good, however I could get neither to work when pasting the following rich text:
This is text after an arrow.
This is a new line
The font would always change to WingDings. I had copied this from MS Word:
Specifically, the plain-text format method described by #idn above did indeed just paste plain text, but something was happening in which the font was changed too.
The following code handles the KeyUp event to just select all text and replace its original colours and font (i.e. formatting). To ensure that this isn't visible on the screen as a flicker, a special method of disabling window repaint events was employed. Control draw disablement occurs in the KeyDown event, the RichTextBox control handles the paste event by itself, and then Control drawing is re-enabled at the end. Finally, this only happens for CTL+V and SHIFT+INS, both of which are standard paste commands:
/// <summary>
/// An application sends the WM_SETREDRAW message to a window to allow changes in that
/// window to be redrawn or to prevent changes in that window from being redrawn.
/// </summary>
private const int WM_SETREDRAW = 11;
private void txtRichTextBox_KeyDown(object sender, KeyEventArgs e)
{
// For supported Paste key shortcut combinations, suspend painting
// of control in preparation for RTF formatting updates on KeyUp
if ((e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.V) || // CTL+V
(!e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Insert)) // SHIFT+INS
{
// Send Suspend Redraw message to avoid flicker. Drawing is
// restored in txtRichTextBox_KeyUp event handler
// [this.SuspendLayout() doesn't work properly]
Message msgSuspendUpdate = Message.Create(
txtRichTextBox.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(txtRichTextBox.Handle);
window.DefWndProc(ref msgSuspendUpdate);
}
}
private void txtRichTextBox_KeyUp(object sender, KeyEventArgs e)
{
// Following supported Paste key shortcut combinations, restore
// original formatting, then resume painting of control.
if ((e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.V) || // CTL+V
(!e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Insert)) // SHIFT+INS
{
// Layout already suspended during KeyDown event
// Capture cursor position. Cursor will later be placed
// after inserted text
int selStart = txtRichTextBox.SelectionStart;
int selLen = txtRichTextBox.SelectionLength;
// Replace all text with original font & colours
txtRichTextBox.SelectAll();
txtRichTextBox.SelectionFont = txtRichTextBox.Font;
txtRichTextBox.SelectionColor = txtRichTextBox.ForeColor;
txtRichTextBox.SelectionBackColor = txtRichTextBox.BackColor;
// Restore original selection
txtRichTextBox.SelectionStart = selStart;
txtRichTextBox.SelectionLength = selLen;
txtRichTextBox.ScrollToCaret();
// Resume painting of control
IntPtr wparam = new IntPtr(1); // Create a C "true" boolean as an IntPtr
Message msgResumeUpdate = Message.Create(
txtRichTextBox.Handle, WM_SETREDRAW, wparam, IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(txtRichTextBox.Handle);
window.DefWndProc(ref msgResumeUpdate);
txtRichTextBox.Invalidate();
txtRichTextBox.Refresh();
}
}
A caveat of this approach is that, because the events are not suppressed (e.Handled = true;), the standard CTL+Z (undo) operation is supported. However this process cycles through undoing the format changes too. I don't see this as a big problem, because the next time that text is pasted, formatting is once again removed.
This approach isn't perfect, because if the text is copied and pasted from the RichTextBox (into another application), the newly applied formatting remains, but in my opinion, that's better than losing the undo functionality. If the undo functionality isn't important, then replace the text selection and formatting application with a replacement of the text to remove all formatting, as per this answer: https://stackoverflow.com/a/1557270/3063884
var t = txtRichTextBox.Text;
txtRichTextBox.Text = t;
In my editor, I have a composite containing a label control right at the top which flashes informative message in red colour whenever the user enters erroneous inputs in any of the below lying fields. The text keeps changing dynamically depending on user's input. I am able to achieve the effect of displaying red coloured text on erroneous inputs and displaying nothing in the label for correct inputs.
But, I want that when there is no error to display in the label composite, the rest of the below fields shift up in display. And when there is error to display, the error should appear in it's place(at the top of all other fields) pushing the other fields down.
Is there a way to achieve this effect without redrawing all the controls again?
Yes, call layout (true) on the parent.
For example I have a view that has a search bar at the top who's visibility can be toggled. I have a method to create the search composite and one to remove it:
private void createNameSearchBar () {
mySearchControl = new CardSearchControl (myViewComposite, SWT.NONE);
mySearchControl.setSearchListener (this);
}
private void disposeNameSearchBar () {
mySearchControl.dispose ();
mySearchControl = null;
}
private CardSearchControl mySearchControl = null;
private Composite myViewComposite;
private boolean mySearchBarState;
To hide or show the search bar control I call this method (myViewComposite is the top level control that owns the search bar and all the other contorls):
public void setSearchBarState (boolean show) {
mySearchBarState = show;
if (myViewComposite == null || myViewComposite.isDisposed ())
return; // no work to do
if (mySearchBarState && mySearchControl == null) {
createNameSearchBar ();
mySearchControl.moveAbove (null);
myViewComposite.layout (true);
} else if (!mySearchBarState && mySearchControl != null) {
disposeNameSearchBar ();
myViewComposite.layout (true);
}
}