(librunview) URL hover-over/visited colouring

This commit is contained in:
Jaidyn Ann 2021-07-15 13:01:21 -05:00
parent 67ea3cd760
commit b7c7ad6064
2 changed files with 123 additions and 39 deletions

View File

@ -9,6 +9,8 @@
#include <Locale.h> #include <Locale.h>
#include <MenuItem.h> #include <MenuItem.h>
#include <PopUpMenu.h> #include <PopUpMenu.h>
#include <ScrollBar.h>
#include <TextView.h>
#include <Window.h> #include <Window.h>
@ -36,6 +38,12 @@ RunView::RunView(const char* name)
urlFont.SetFace(B_UNDERSCORE_FACE); urlFont.SetFace(B_UNDERSCORE_FACE);
text_run urlRun = { 0, urlFont, ui_color(B_LINK_TEXT_COLOR) }; text_run urlRun = { 0, urlFont, ui_color(B_LINK_TEXT_COLOR) };
fUrlRun = { 1, {urlRun} }; fUrlRun = { 1, {urlRun} };
text_run urlHoverRun = { 0, urlFont, ui_color(B_LINK_HOVER_COLOR) };
fUrlHoverRun = { 1, {urlHoverRun} };
text_run urlVisitedRun = { 0, urlFont, ui_color(B_LINK_VISITED_COLOR) };
fUrlVisitedRun = { 1, {urlVisitedRun} };
} }
@ -87,35 +95,27 @@ RunView::Insert(const char* text, const text_run_array* runs)
{ {
BString buf(text); BString buf(text);
int32 urlOffset = 0; int32 specStart = 0;
int32 specEnd = 0;
int32 lastEnd = 0; int32 lastEnd = 0;
while (lastEnd < buf.CountChars() && urlOffset != B_ERROR) int32 length = buf.CountChars();
{
urlOffset = buf.FindFirst("://", lastEnd);
int32 urlStart = buf.FindLast(" ", urlOffset) + 1; while (_FindUrlString(buf, &specStart, &specEnd, lastEnd) == true) {
int32 urlEnd = buf.FindFirst(" ", urlOffset); if (lastEnd < specStart) {
if (urlStart == B_ERROR || urlOffset == B_ERROR) BString normie;
urlStart = lastEnd; buf.CopyCharsInto(normie, lastEnd, specStart - lastEnd);
if (urlEnd == B_ERROR || urlOffset == B_ERROR) BTextView::Insert(normie.String(), runs);
urlEnd = buf.CountChars();
BString url;
BString nonurl;
if (urlOffset == B_ERROR)
buf.CopyCharsInto(nonurl, urlStart, urlEnd - urlStart);
else {
buf.CopyCharsInto(nonurl, lastEnd, urlStart - lastEnd);
buf.CopyCharsInto(url, urlStart, urlEnd - urlStart);
} }
BString special;
buf.CopyCharsInto(special, specStart, specEnd - specStart);
BTextView::Insert(special.String(), &fUrlRun);
// Actually insert the text lastEnd = specEnd;
if (nonurl.IsEmpty() == false) }
BTextView::Insert(nonurl.String(), runs); if (lastEnd < length) {
if (url.IsEmpty() == false) BString remaining;
BTextView::Insert(url.String(), &fUrlRun); buf.CopyCharsInto(remaining, lastEnd, length - lastEnd);
BTextView::Insert(remaining.String(), runs);
lastEnd = urlEnd;
} }
} }
@ -131,12 +131,14 @@ RunView::MouseDown(BPoint where)
else else
BTextView::MouseDown(where); BTextView::MouseDown(where);
BUrl url; if ((buttons & B_PRIMARY_MOUSE_BUTTON) && OverUrl(where)) {
if ((buttons & B_PRIMARY_MOUSE_BUTTON) && OverUrl(where, &url)) {
fMouseDown = true; fMouseDown = true;
if (url.IsValid() == true)
BUrl url(WordAt(where));
if (url.IsValid() == true) {
fLastClicked = url; fLastClicked = url;
} }
}
} }
@ -148,6 +150,8 @@ RunView::MouseUp(BPoint where)
if (fMouseDown && fSelecting == false && fLastClicked.IsValid() == true) { if (fMouseDown && fSelecting == false && fLastClicked.IsValid() == true) {
fLastClicked.OpenWithPreferredApplication(true); fLastClicked.OpenWithPreferredApplication(true);
fLastClicked = BUrl(); fLastClicked = BUrl();
// When cursor moves off URL, change color
fCurrentUrlRuns = fUrlVisitedRun;
} }
fMouseDown = false; fMouseDown = false;
fSelecting = false; fSelecting = false;
@ -160,11 +164,29 @@ RunView::MouseMoved(BPoint where, uint32 code, const BMessage* drag)
if (fSelecting == true) if (fSelecting == true)
return; return;
// Change the cursor and "hover-over" highlight for URLs
if (code == B_INSIDE_VIEW) if (code == B_INSIDE_VIEW)
if (OverUrl(where) == true) if (OverUrl(where) == true) {
int32 start = 0;
int32 end = 0;
FindWordAround(OffsetAt(where), &start, &end);
if (fCurrentUrlEnd == 0) {
fCurrentUrlRuns = *RunArray(start, end);
fCurrentUrlStart = start;
fCurrentUrlEnd = end;
ReplaceRuns(start, end, &fUrlHoverRun);
}
SetViewCursor(fUrlCursor); SetViewCursor(fUrlCursor);
else }
else {
if (fCurrentUrlEnd != 0) {
ReplaceRuns(fCurrentUrlStart, fCurrentUrlEnd, &fCurrentUrlRuns);
fCurrentUrlStart = 0;
fCurrentUrlEnd = 0;
}
SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
}
} }
@ -202,6 +224,26 @@ RunView::Append(const char* text)
} }
void
RunView::Replace(int32 start, int32 end, const char* text, text_run_array* runs)
{
Delete(start, end);
BTextView::Insert(start, text, strlen(text), runs);
}
void
RunView::ReplaceRuns(int32 start, int32 end, text_run_array* runs)
{
char* buffer = new char[end - start];
GetText(start, end - start, buffer);
float current = ScrollBar(B_VERTICAL)->Value();
Replace(start, end, buffer, runs);
ScrollBar(B_VERTICAL)->SetValue(current);
}
BString BString
RunView::WordAt(BPoint point) RunView::WordAt(BPoint point)
{ {
@ -265,17 +307,23 @@ RunView::OverText(BPoint where)
bool bool
RunView::OverUrl(BPoint where, BUrl* url) RunView::OverUrl(BPoint where)
{ {
if (OverText(where) == false) if (OverText(where) == false)
return false; return false;
BString word = WordAt(where); int32 offset = OffsetAt(where);
if (BUrl(word.String()).IsValid() == true) { text_run_array* rArray = RunArray(offset, offset + 1);
if (url != NULL) text_run run = rArray->runs[0];
url->SetUrlString(word); text_run urlRun = fUrlRun.runs[0];
text_run urlHover = fUrlHoverRun.runs[0];
text_run urlVisit = fUrlVisitedRun.runs[0];
if ((run.font == urlRun.font || run.font == urlHover.font
|| run.font == urlVisit.font)
&& (run.color == urlRun.color || run.color == urlHover.color
|| run.color == urlVisit.color))
return true; return true;
}
return false; return false;
} }
@ -323,3 +371,21 @@ RunView::_RightClickPopUp(BPoint where)
menu->SetTargetForItems(this); menu->SetTargetForItems(this);
return menu; return menu;
} }
bool
RunView::_FindUrlString(BString text, int32* start, int32* end, int32 offset)
{
int32 urlOffset = text.FindFirst("://", offset);
int32 urlStart = text.FindLast(" ", urlOffset) + 1;
int32 urlEnd = text.FindFirst(" ", urlOffset);
if (urlStart == B_ERROR) urlStart = 0;
if (urlEnd == B_ERROR) urlEnd = text.CountChars();
if (urlOffset != B_ERROR) {
*start = urlStart;
*end = urlEnd;
return true;
}
return false;
}

View File

@ -30,24 +30,42 @@ public:
uint16 fontFace = B_REGULAR_FACE); uint16 fontFace = B_REGULAR_FACE);
void Append(const char* text); void Append(const char* text);
void Replace(int32 start, int32 end, const char* text,
text_run_array* runs);
void ReplaceRuns(int32 start, int32 end, text_run_array* runs);
BString WordAt(BPoint point); BString WordAt(BPoint point);
void FindWordAround(int32 offset, int32* start, int32* end, void FindWordAround(int32 offset, int32* start, int32* end,
BString* _word = NULL); BString* _word = NULL);
const char* GetLine(int32 line); const char* GetLine(int32 line);
bool OverText(BPoint where); bool OverText(BPoint where);
bool OverUrl(BPoint where, BUrl* url = NULL); bool OverUrl(BPoint where);
void ScrollToBottom(); void ScrollToBottom();
private: private:
BPopUpMenu* _RightClickPopUp(BPoint where); BPopUpMenu* _RightClickPopUp(BPoint where);
bool fLastStyled; bool _FindUrlString(BString text, int32* start, int32* end,
int32 offset);
// For safe-keeping
text_run_array fDefaultRun; text_run_array fDefaultRun;
text_run_array fUrlRun; text_run_array fUrlRun;
text_run_array fUrlHoverRun;
text_run_array fUrlVisitedRun;
BCursor* fUrlCursor; BCursor* fUrlCursor;
// Whether or not the run was changed from default
bool fLastStyled;
// Used for the "hover over" URL highlighting
text_run_array fCurrentUrlRuns;
int32 fCurrentUrlStart;
int32 fCurrentUrlEnd;
// Information between MouseDown and MouseUp
BUrl fLastClicked; BUrl fLastClicked;
bool fMouseDown; bool fMouseDown;
bool fSelecting; bool fSelecting;