Custom links in RichTextBox - c#

Suppose I want every word starting with a # to generate an event on double click. For this I have implemented the following test code:
private bool IsChannel(Point position, out int start, out int end)
{
if (richTextBox1.Text.Length == 0)
{
start = end = -1;
return false;
}
int index = richTextBox1.GetCharIndexFromPosition(position);
int stop = index;
while (index >= 0 && richTextBox1.Text[index] != '#')
{
if (richTextBox1.Text[index] == ' ')
{
break;
}
--index;
}
if (index < 0 || richTextBox1.Text[index] != '#')
{
start = end = -1;
return false;
}
while (stop < richTextBox1.Text.Length && richTextBox1.Text[stop] != ' ')
{
++stop;
}
--stop;
start = index;
end = stop;
return true;
}
private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
textBox1.Text = richTextBox1.GetCharIndexFromPosition(new Point(e.X, e.Y)).ToString();
int d1, d2;
if (IsChannel(new Point(e.X, e.Y), out d1, out d2) == true)
{
if (richTextBox1.Cursor != Cursors.Hand)
{
richTextBox1.Cursor = Cursors.Hand;
}
}
else
{
richTextBox1.Cursor = Cursors.Arrow;
}
}
This handles detecting words that start with # and making the mouse cursor a hand when it hovers over them. However, I have the following two problems:
If I try to implement a double click event for richTextBox1, I can detect when a word is clicked, however that word is highlighted (selected), which I'd like to avoid. I can deselect it programmatically by selecting the end of the text, but that causes a flicker, which I would like to avoid. What ways are there to do this?
The GetCharIndexFromPosition method returns the index of the character that is closest to the cursor. This means that if the only thing my RichTextBox contains is a word starting with a # then the cursor will be a hand no matter where on the rich text control it is. How can I make it so that it is only a hand when it hovers over an actual word or character that is part of a word I'm interested in? The implemented URL detection also partially suffers from this problem. If I enable detection of URLs and only write www.test.com in the rich text editor, the cursor will be a hand as long as it is on or below the link. It will not be a hand if it's to the right of the link however. I'm fine even with this functionality if making the cursor a hand if and only if it's on the text proves to be too difficult.
I'm guessing I'll have to resort to some sort of Windows API calls, but I don't really know where to start.
I am using Visual Studio 2008 and I would like to implement this myself.
Update:
The flickering problem would be solved if I could make it so that no text is selectable through double clicking, only through dragging the mouse cursor and programmatically. Is this easier to achieve? Because I don't really care if one can select text or not by double clicking in this case.

On point (2) you could try:
After if (richTextBox1.Text.Length == 0){ ... }
//get the mouse point in client coordinates
Point clientPoint = richTextBox1.PointToClient(richTextBox1.PointToScreen(position));
int index = richTextBox1.GetCharIndexFromPosition(position);
//get the position of the closest char
Point charPoint = richTextBox1.GetPositionFromCharIndex(index);
bool notOnTheSameLine = ((clientPoint.Y < charPoint.Y) || (clientPoint.Y > charPoint.Y + richTextBox1.Font.Height));
bool passedTheWord = (clientPoint.X > charPoint.X + richTextBox1.Font.SizeInPoints);
if (notOnTheSameLine || passedTheWord)
{
start = end = -1;
return false;
}
For point (1) maybe have a different way of following the link than dbl-click? Maybe cntl-click would avoid the issues of the word becoming selected...

Related

Get Movement Direction of Mouse

I know this has been Asked but in my Case i dont know if i Can use the MouseEventArgs since its a Rendering / Input Loop where Im trying to use it Here is the Whole Rendering / Input Loop, I can only Move with the Keyboard which is Rather inconvinient.
public void update()
{
//--do raycast--//
raycast();
//=========================//
//=====take user input=====//
//=========================//
KeyboardState state = Keyboard.GetState();
bool lArrowKeyDown = state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A);
//MouseLeft
if (lArrowKeyDown)
{
rotate(rotSpeed);
}
//MouseRight
bool rArrowKeyDown = state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D);
if (rArrowKeyDown)
{
rotate(-rotSpeed);
}
//MouseUp
bool uArrowKeyDown = state.IsKeyDown(Keys.Up) || state.IsKeyDown(Keys.W);
bool shift = state.IsKeyDown(Keys.LeftShift);
if (uArrowKeyDown)
{
move(moveSpeed);
}
if (uArrowKeyDown && shift)
{
move(moveSpeed+0.01);
}
bool dArrowKeyDown = state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S);
//MouseDown
if (dArrowKeyDown)
{
move(-moveSpeed);
}
//=========================//
//=====user input end======//
//=========================//
}
This is the Whole Rendering / Input Loop, Would it be Possible to add a MouseEventArgs? Im unsure of if a MouseEventArgs Loops too when this Loops. Could someone help me Here?
I’m not entirely sure what you are asking for, but if you want to get movement direction from a mouse event, unless the mouse event has a last position and new position property, you need to keep track of the last position (x and y value) and use it with the current position to calculate the angle.
For calculating the angle, see
Calculating the angle between the line defined by two points
I can provide a better answer with more information.

Zoom/unzoom and points decimation

Situation:
I have one Chart and three ChartArea that are aligned in view, zoom, cursor through the AxisViewChanged method that act in this way:
if (e.Axis == ax1)
{
ax2.ScaleView.Size = ax1.ScaleView.Size;
ax2.ScaleView.Position = ax1.ScaleView.Position;
ax3.ScaleView.Size = ax1.ScaleView.Size;
ax3.ScaleView.Position = ax1.ScaleView.Position;
min = (int)ax1.ScaleView.ViewMinimum;
max = (int)ax1.ScaleView.ViewMaximum;
}
if (e.Axis == ax2)
{
....
And it works very well in both cases: when I zoom in/out or scroll.
Problem:
The problem is that my graph source is made by a lot of points, in the worst case we talk about 3'600'000 samples. With this amount of samples, when I move around points with cursor and try to show a tooltip with the values, the interaction quality collapse and becomes unusables (even having set Fast Line).
So I tried to implement a simple decimation algorithm to reducethe number of showed points:
void draw_graph(int start, int end)
{
double fract_value = 0;
int int_value = 0;
int num_saples_selected = end - start;
if(num_saples_selected <= MAX_GRAPH_NUM_SAMPLES)
fill_graphs_point(0, end, 1);
else
{
fract_value = ((double)num_saples_selected) / ((double)MAX_GRAPH_NUM_SAMPLES);
int_value = (int)fract_value;
if (fract_value > int_value)
int_value++;
fill_graphs_point(0, end, int_value);
}
}
void fill_graphs_point(int start, int end, int step)
{
int i = 0;
for (i = start; i < end; i=i+step)
{
dlChart.Series[SERIES_VOLTAGE].Points.AddXY(timestamps_array[i], voltage_array[i]);
dlChart.Series[SERIES_CURRENT].Points.AddXY(timestamps_array[i], current_array[i]);
dlChart.Series[SERIES_ENERGY].Points.AddXY(timestamps_array[i], energy_array[i]);
// I will use this to came back to real position of the initial array
decimation_positions.Add(i);
}
}
Assuminig I had a good idea with this method to reduce the points number, I do not know where to put the call to the function "draw_graph". If I put it in the AxisViewChanged method it will call my method also when I scroll (horizontally) my graph and this is not what I want. I want to call my method only on zoom and unzoom event.
Expected behavior: in the first view (without any zoom) the graph has to show an "idea" of the trend of the graph. Then for every selection/(un)zoom I want to call my function to check if the points number of selected portion will fit in my window size that is MAX_GRAPH_NUM_SAMPLES(=10000).
Hope someone can help me. Whatever kind of suggestion will be appreciated. Thanks in advance.
I forgot to say that strangely the problem appeared when I zoomed in more than one time. At some point also the PC fan started. I resolved disabling the library zoom and implement my self a sort of zoom (a little bit more simple). The solution is in this method and the method I write in the question:
private void dlChart_SelectionRangeChange(object sender, CursorEventArgs e)
{
double startSelection = e.NewSelectionStart;
double endSelection = e.NewSelectionEnd;
// Allow the selection to disappear
reset_selection();
try
{
// before convert point from "showed point" to "real point" I check the limits
if (
(startSelection >= 0) && (startSelection <= decimation_positions.Count()) &&
(endSelection >= 0) && (endSelection <= decimation_positions.Count()) &&
(endSelection != startSelection)
)
{
// convert
startSelection = decimation_positions[(int)startSelection];
endSelection = decimation_positions[(int)endSelection];
// check for reverse zoom (from right to left)
if (startSelection > endSelection)
{
double tmp = startSelection;
startSelection = endSelection;
endSelection = tmp;
}
// clean the series
this.reset_series();
// draw the selected range
draw_graph((int)startSelection, (int)endSelection);
}
}catch(ArgumentOutOfRangeException ex)
{
// todo
}
//System.Console.WriteLine("SelSTART = "+e.NewSelectionStart+" SelEND = "+e.NewSelectionEnd);
}

Kinect: Grab/Release

I'm a beginner in Kinect Programming. Yet I read different tutorials and texts about it. Furthermore I was working on the code examples ("Controls Basis-WPF").
Unfortunately none of the sources teach me how to code grabbing and releasing in a way that I understand.
I would like to create dynamically KinectTileButtons. While the program is running the user should be able to place the buttons to the positions he prefers. The player uses his hand as the cursor.
For example:
There is a button b1 on the top left corner.
While the hand of the user is not on b1, the button doesn't change his position.
If the user's hand is on the button AND he makes the "GRAB"-gesture, the position of the button is like the position of his hand (variable position). When he RELEASES, the position of button is constant and will not change until the user is grabbing for the button the next time.
I'm very thankful for any advices, suggestions or code examples, because I really don't know how I should continue to work on this problem.
Thanks in advance
derpate
What you should be going for is to change the location of the buttons on the window. I would have some global bools to signify is they are grabbing and holding. Then I would put a conditional in the update to move the label to the position of the hand. Here's some pseudo-code:
bool grabbing = false;
bool selected = false;
var button;
var handElement;
Start()
{
button = window.Button;
handElement = window.handElement;
...
sensor.Start();
}
Update() //all frames ready
{
using (SkeletonFrame sf = e.OpenSkeletonFrame())
{
... //get skeleton and position
... //tell if grabbing
if (/*however you do the grab gesture*/)
grabbing = true;
if (selected && grabbing)
{
button.X = handElement.X;
button.Y = handElement.Y;
}
}
}
Button_OnClick()//or whatever it is called for the Kinect UI control
{
selected = true;
}
Sorry for the pseudo-code, I just haven't worked with the Kinect UI before, and I didn't want to have half pseudo-code with the elements I don't know, however I know that it has events like OnClick() for when it is held, and I don't know what you mean by "GRAB" gesture but I assume you know how to implement it if you know what it is. Good luck!
Know also this only works for one button. If you were doing this for many, I would suggest a class... something like:
class MoveableButton : KinectButton//again, don't know the exact name of this
{
private int X, Y;
private bool selected = false, grabbing = false;
public void Selected()
{
selected = true;
}
public void Grabbed()
{
grabbing = true;
}
public void LetGo()
{
grabbing = false;
selected = false;
}
public void Update(UIElement hand)
{
if (selected && grabbing)
{
this.X = hand.X;
this.Y = hand.Y;
}
}
}
This you would then call the appropriate methods during certain frames, I would have an array of them to loop through for Update.

RichTextBox formatting and restoring cursor and scrollbar position

I have RichTextBox in which I format text, I have there multiple text selections and formats.
Because of that, after the formatting is done, the scrollbars positions of RichTextBox isn't the same.
How am I able to save and restore scrollbar position same (easy) way as I can save cursor position?
protected override void OnTextChanged(EventArgs e)
{
// Save cursor position
int cursor_position = this.SelectionStart;
// Format text
Highlight();
// Restore position
this.SelectionLength = 0;
this.SelectionStart = cursor_position;
}
I have seen many posts here that were solving this by handling scroll messages.
I have managed this simplier way, so if anyone have the same problem, you can use this way. It is not perfect (if there is a half of line displayed at the top, it will be scrolled), but is enough I think :).
protected override void OnTextChanged(EventArgs e)
{
// Get first and last displayed character
int start = this.GetCharIndexFromPosition(new Point(0, 0));
int end = this.GetCharIndexFromPosition(new Point(this.ClientSize.Width, this.ClientSize.Height));
// Save cursor position
int cursor_position = this.SelectionStart;
int cursor_lenght = this.SelectionLength;
// Your formatting
Highlight();
// Scroll to the last character and then to the first + line width
this.SelectionLength = 0;
this.SelectionStart = end;
this.ScrollToCaret();
this.SelectionStart = start + this.Lines[this.GetLineFromCharIndex(start)].Length+1;
this.ScrollToCaret();
// Finally, set cursor to original position
this.SelectionStart = cursor_position;
}

TouchLocation object not working properly

I am trying to implement a press and release button functionality and am getting a non-working result that I can't figure out why.
public class Button
{
public TouchLocation oldTouch, currentTouch;
public virtual void Update(GameTime gameTime)
{
TouchCollection touchCollection = TouchPanel.GetState ();
if (touchCollection.Count > 0) {
currentTouch = touchCollection [0];
if (currentTouch.Position.X > position.X && currentTouch.Position.X < position.X + width &&
currentTouch.Position.Y > position.Y && currentTouch.Position.Y < position.Y + height) {
if (currentTouch.State == TouchLocationState.Released && oldTouch.State == TouchLocationState.Pressed) {
buttonEvent.Invoke (this, new EventArgs ());
}
}
}
oldTouch = currentTouch;
}
}
First of all when I assign the below object:
currentTouch = touchCollection [0];
I get
"Incorrect number of types or arguments"
in red as the value in the locals debug window (no error, just red texted value).
It still however correctly passes through the position checking IF statement but it does not pass the Pressed/Release IF statement. If I replace currentTouch with touchCollection[0] then everything works as expected.
Am I using/assigning the TouchLocation object incorrectly?
Or am I maybe just overlooking an easy mistake?
I always use something like touchCollection.Last() or touchCollection.First() to get only one component of TouchCollection. Anyway I don't see any error in what you are doing there.
For your problem, I think you can't do only
oldTouch.State == TouchLocationState.Pressed
but you have to check:
oldTouch.State == TouchLocationState.Pressed || oldTouch.State == TouchLocationState.Moved
because you don't know how the last touch was detected by XNA.
And as a suggestion, why don't you use Rectangles for your button's collider? In this way you can simply call:
Rectangle.Contains(new Point(int)currentTouch.Position.X, (int)currentTouch.Position.Y))
instead of doing that if statement.

Categories