Added "tooltip" property

This commit is contained in:
anoshenko 2023-04-25 17:20:47 +03:00
parent ac8bb47677
commit a83d634f54
12 changed files with 267 additions and 2 deletions

View File

@ -1472,6 +1472,12 @@ radius необходимо передать nil
| 18 | "blockquote" | Цитата. Изменяет стиль текста |
| 19 | "code" | Программный код. Изменяет стиль текста |
### Свойство "tooltip"
Свойство "tooltip" (константа Tooltip) типа string задает текст всплывающей подсказки.
Подсказка всплывает при наведении курсора мыши. При оформление текста подсказки можно
использовать html тэги
### Свойства текста
Все перечисленные в этом разделе свойства являются наследуемыми, т.е. свойство будет применяться не только ко View
@ -5591,6 +5597,9 @@ Safari и Firefox.
| ruiPopupTextColor | Цвет текста всплывающего окна |
| ruiPopupTitleColor | Цвет фона заголовка всплывающего окна |
| ruiPopupTitleTextColor | Цвет текста заголовка всплывающего окна |
| ruiTooltipBackground | Цвет фона всплывающей подсказки |
| ruiTooltipTextColor | Цвет текста всплывающей подсказки |
| ruiTooltipShadowColor | Цвет тени всплывающей подсказки |
Константы которые вы можете переопределять:

View File

@ -1453,6 +1453,12 @@ It also helps to voice the interface to systems for people with disabilities:
| 18 | "blockquote" | Quote. Changes the style of the text |
| 19 | "code" | Program code. Changes the style of the text |
### "tooltip" property
The "tooltip" string property (Tooltip constant) specifies the tooltip text.
Tooltip pops up when hovering the mouse cursor.
You can use html tags when formatting the tooltip text.
### Text properties
All properties listed in this section are inherited, i.e. the property will apply
@ -5542,6 +5548,9 @@ System color constants that you can override:
| ruiPopupTextColor | Popup text color |
| ruiPopupTitleColor | Popup title background color |
| ruiPopupTitleTextColor | Popup Title Text Color |
| ruiTooltipBackground | Tooltip background color |
| ruiTooltipTextColor | Tooltip text color |
| ruiTooltipShadowColor | Tooltip shadow color |
Constants that you can override:

View File

@ -1877,3 +1877,124 @@ function localStorageClear() {
sendMessage("storageError{session=" + sessionID + ", error=`" + err + "`}")
}
}
function showTooltip(element, tooltip) {
const layer = document.getElementById("ruiTooltipLayer");
if (!layer) {
return;
}
layer.style.left = "0px";
layer.style.right = "0px";
var tooltipBox = document.getElementById("ruiTooltipText");
if (tooltipBox) {
tooltipBox.innerHTML = tooltip;
}
var left = element.offsetLeft;
var top = element.offsetTop;
var width = element.offsetWidth;
var height = element.offsetHeight;
var parent = element.offsetParent;
while (parent) {
left += parent.offsetLeft;
top += parent.offsetTop;
width = parent.offsetWidth;
height = parent.offsetHeight;
parent = parent.offsetParent;
}
if (element.offsetWidth >= tooltipBox.offsetWidth) {
layer.style.left = left + "px";
layer.style.justifyItems = "start";
} else {
const rightOff = width - (left + element.offsetWidth);
if (left > rightOff) {
if (width - left < tooltipBox.offsetWidth) {
layer.style.right = rightOff + "px";
layer.style.justifyItems = "end";
} else {
layer.style.left = (left - rightOff) + "px";
layer.style.right = "0px";
layer.style.justifyItems = "center";
}
} else {
if (width - rightOff < tooltipBox.offsetWidth) {
layer.style.left = left + "px";
layer.style.justifyItems = "start";
} else {
layer.style.right = (rightOff - left) + "px";
layer.style.justifyItems = "center";
}
}
}
const bottomOff = height - (top + element.offsetHeight);
var arrow = document.getElementById("ruiTooltipTopArrow");
if (bottomOff < arrow.offsetHeight + tooltipBox.offsetHeight) {
if (arrow) {
arrow.style.visibility = "hidden";
}
arrow = document.getElementById("ruiTooltipBottomArrow");
if (arrow) {
arrow.style.visibility = "visible";
}
layer.style.top = "0px";
layer.style.bottom = height - top - arrow.offsetHeight / 2 + "px";
layer.style.gridTemplateRows = "1fr auto auto"
} else {
if (arrow) {
arrow.style.visibility = "visible";
}
layer.style.top = top + element.offsetHeight - arrow.offsetHeight / 2 + "px";
layer.style.bottom = "0px";
layer.style.gridTemplateRows = "auto auto 1fr"
arrow = document.getElementById("ruiTooltipBottomArrow");
if (arrow) {
arrow.style.visibility = "hidden";
}
}
layer.style.visibility = "visible";
layer.style.opacity = 1;
}
function mouseEnterEvent(element, event) {
event.stopPropagation();
let tooltip = element.getAttribute("data-tooltip");
if (tooltip) {
showTooltip(element, tooltip);
}
sendMessage("mouse-enter{session=" + sessionID + ",id=" + element.id + mouseEventData(element, event) + "}");
}
function mouseLeaveEvent(element, event) {
event.stopPropagation();
if (element.getAttribute("data-tooltip")) {
const layer = document.getElementById("ruiTooltipLayer");
if (layer) {
layer.style.opacity = 0;
layer.style.visibility = "hidden";
}
}
sendMessage("mouse-leave{session=" + sessionID + ",id=" + element.id + mouseEventData(element, event) + "}");
}
function setCssVar(tag, value) {
const root = document.querySelector(':root');
if (root) {
root.style.setProperty(tag, value);
}
}

View File

@ -8,6 +8,13 @@
text-overflow: ellipsis;
}
:root {
--tooltip-arrow-size: 6px;
--tooltip-background: white;
--tooltip-text-color: black;
--tooltip-shadow-color: gray;
}
body {
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -76,7 +83,54 @@ ul:focus {
left: 0px;
}
.ruiTooltipLayer {
display: grid;
grid-template-rows: 1fr auto 1fr;
justify-items: center;
align-items: center;
position: absolute;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
transition: opacity 0.5s ease-out;
filter: drop-shadow(0px 0px 2px var(--tooltip-shadow-color));
}
.ruiTooltipTopArrow {
grid-row-start: 1;
grid-row-end: 2;
border-width: var(--tooltip-arrow-size);
border-style: solid;
border-color: transparent transparent var(--tooltip-background) transparent;
margin-left: 12px;
margin-right: 12px;
}
.ruiTooltipBottomArrow {
grid-row-start: 3;
grid-row-end: 4;
border-width: var(--tooltip-arrow-size);
border-style: solid;
border-color: var(--tooltip-background) transparent transparent transparent;
margin-left: 12px;
margin-right: 12px;
}
.ruiTooltipText {
grid-row-start: 2;
grid-row-end: 3;
padding: 4px 8px 4px 8px;
margin-left: 8px;
margin-right: 8px;
background-color: var(--tooltip-background);
color: var(--tooltip-text-color);
/*box-shadow: 0px 0px 4px 2px #8888;*/
border-radius: 4px;
}
.ruiView {
box-sizing: border-box;
}
.ruiAbsoluteLayout {

View File

@ -76,6 +76,12 @@ func getStartPage(buffer *strings.Builder, params AppParams, addScripts string)
<body>
<div class="ruiRoot" id="ruiRootView"></div>
<div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden; isolation: isolate;" onclick="clickOutsidePopup(event)"></div>
<div class="ruiTooltipLayer" id="ruiTooltipLayer" style="visibility: hidden; opacity: 0;">
<div id="ruiTooltipText" class="ruiTooltipText"></div>
<div id="ruiTooltipTopArrow" class="ruiTooltipTopArrow"></div>
<div id="ruiTooltipBottomArrow" class="ruiTooltipBottomArrow"></div>
</div>
<a id="ruiDownloader" download style="display: none;"></a>
</body>`)
}

View File

@ -23,6 +23,9 @@ theme {
ruiTabTextColor = #FF404040,
ruiCurrentTabColor = #FFFFFFFF,
ruiCurrentTabTextColor = #FF000000,
ruiTooltipBackground = #FFFFFFFF,
ruiTooltipTextColor = #FF000000,
ruiTooltipShadowColor = #FF808080,
},
colors:dark = _{
ruiTextColor = #FFE0E0E0,
@ -43,6 +46,9 @@ theme {
ruiTabTextColor = #FFE0E0E0,
ruiCurrentTabColor = #FF000000,
ruiCurrentTabTextColor = #FFFFFFFF,
ruiTooltipBackground = #FF303030,
ruiTooltipTextColor = #FFDDDDDD,
ruiTooltipShadowColor = #FFDDDDDD,
},
constants = _{
ruiButtonHorizontalPadding = 16px,

View File

@ -184,7 +184,7 @@ func (view *viewData) removeMouseListener(tag string) {
}
}
func mouseEventsHtml(view View, buffer *strings.Builder) {
func mouseEventsHtml(view View, buffer *strings.Builder, hasTooltip bool) {
for tag, js := range mouseEvents {
if value := view.getRaw(tag); value != nil {
if listeners, ok := value.([]func(View, MouseEvent)); ok && len(listeners) > 0 {
@ -192,6 +192,11 @@ func mouseEventsHtml(view View, buffer *strings.Builder) {
}
}
}
if hasTooltip {
buffer.WriteString(`onmouseenter="mouseEnterEvent(this, event)" `)
buffer.WriteString(`onmouseleave="mouseLeaveEvent(this, event)" `)
}
}
func getTimeStamp(data DataObject) uint64 {

View File

@ -637,6 +637,7 @@ func (manager *popupManager) showPopup(popup Popup) {
session.callFunc("blurCurrent")
manager.updatePopupLayerInnerHTML(session)
session.updateCSSProperty("ruiTooltipLayer", "visibility", "hidden")
session.updateCSSProperty("ruiPopupLayer", "visibility", "visible")
session.updateCSSProperty("ruiRoot", "pointer-events", "none")
}

View File

@ -702,4 +702,6 @@ const (
// * tabindex="0" means that View should be focusable in sequential keyboard navigation, after any positive tabindex values and its order is defined in order of its addition.
// * A positive value means View should be focusable in sequential keyboard navigation, with its order defined by the value of the number.
TabIndex = "tabindex"
Tooltip = "tooltip"
)

View File

@ -303,6 +303,21 @@ func (session *sessionData) writeInitScript(writer *strings.Builder) {
writer.WriteString(text)
writer.WriteString("';\nscanElementsSize();")
}
session.updateTooltipConstants()
}
func (session *sessionData) updateTooltipConstants() {
if color, ok := session.Color("ruiTooltipBackground"); ok {
session.bridge.callFunc("setCssVar", "--tooltip-background", color.cssString())
}
if color, ok := session.Color("ruiTooltipTextColor"); ok {
session.bridge.callFunc("setCssVar", "--tooltip-text-color", color.cssString())
}
if color, ok := session.Color("ruiTooltipShadowColor"); ok {
session.bridge.callFunc("setCssVar", "--tooltip-shadow-color", color.cssString())
}
}
func (session *sessionData) reload() {
@ -323,6 +338,7 @@ func (session *sessionData) reload() {
}
session.bridge.writeMessage(buffer.String())
session.updateTooltipConstants()
}
func (session *sessionData) ignoreViewUpdates() bool {

20
view.go
View File

@ -634,6 +634,16 @@ func viewPropertyChanged(view *viewData, tag string) {
session.updateCSSProperty(htmlID, `column-span`, `none`)
}
return
case Tooltip:
if tooltip := GetTooltip(view); tooltip == "" {
session.removeProperty(htmlID, "data-tooltip")
} else {
session.updateProperty(htmlID, "data-tooltip", tooltip)
session.updateProperty(htmlID, "onmouseenter", "mouseEnterEvent(this, event)")
session.updateProperty(htmlID, "onmouseleave", "mouseLeaveEvent(this, event)")
}
return
}
if cssTag, ok := sizeProperties[tag]; ok {
@ -796,10 +806,18 @@ func viewHTML(view View, buffer *strings.Builder) {
}
}
hasTooltip := false
if tooltip := GetTooltip(view); tooltip != "" {
buffer.WriteString(`data-tooltip=" `)
buffer.WriteString(tooltip)
buffer.WriteString(`" `)
hasTooltip = true
}
buffer.WriteString(`onscroll="scrollEvent(this, event)" `)
keyEventsHtml(view, buffer)
mouseEventsHtml(view, buffer)
mouseEventsHtml(view, buffer, hasTooltip)
pointerEventsHtml(view, buffer)
touchEventsHtml(view, buffer)
focusEventsHtml(view, buffer)

View File

@ -945,3 +945,21 @@ func GetMixBlendMode(view View, subviewID ...string) int {
func GetBackgroundBlendMode(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, BackgroundBlendMode, BlendNormal, true)
}
// GetTooltip returns a tooltip text of the subview.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTooltip(view View, subviewID ...string) string {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.Get(Tooltip); value != nil {
if text, ok := value.(string); ok {
return text
}
}
}
return ""
}