tooltip UI

This commit is contained in:
Val Erastov 2016-10-13 00:38:37 -07:00
parent e5485dd9be
commit 941edf908d
10 changed files with 114 additions and 43 deletions

View file

@ -38,8 +38,10 @@
"webpack-dev-server": "1.15.0"
},
"dependencies": {
"sprintf": "0.1.5",
"diff-match-patch": "1.0.0",
"numeric": "1.2.6",
"jwerty": "0.3.2"
"jwerty": "0.3.2",
"mustache-loader": "0.3.3"
}
}

View file

@ -66,7 +66,7 @@ ActionManager.prototype.run = function(actionId, event) {
if (action.state.enabled) {
action.__handler(this.app, event);
} else {
this.app.inputManager.info("action '"+actionId+"' is disabled and can't be launched<br>" + action.state.hint);
this.app.inputManager.messageSink.info("action '"+actionId+"' is disabled and can't be launched<br>" + action.state.hint);
}
};

26
web/app/3d/ui/bind.js Normal file
View file

@ -0,0 +1,26 @@
import {sprintf} from 'sprintf'
export function Bind(dom, data, policy) {
if (!policy) policy = DEFAULT_POLICY;
const props = Object.getOwnPropertyNames(data);
for (let prop of props) {
const node = $(dom).find('[data-bind="'+prop+'"]');
if (node.length == 0) continue;
let value = data[prop];
if (!policy.hideEmptyValue || value || value === 0) {
var format = node.attr('bind-format');
if (format) {
value = sprintf(format, value);
}
node.text(value);
node.show();
} else {
node.text('');
node.hide();
}
}
}
const DEFAULT_POLICY = {
hideEmptyValue: true
};

View file

@ -1,6 +1,8 @@
import {jwerty} from 'jwerty'
import {keymap} from './keymaps/default'
import {DefaultMouseEvent, EventData, fit} from './utils'
import {Bind} from './bind'
import {MessageSink} from './message-sink'
import {LoadTemplate, DefaultMouseEvent, EventData, fit} from './utils'
export function InputManager(app) {
this.app = app;
@ -8,19 +10,20 @@ export function InputManager(app) {
this.keymap = keymap;
this.mouseInfo = new DefaultMouseEvent();
this.requestedActionInfo = null;
this.actionInfoDom = $(LoadTemplate('action-info')({}));
this.messageSink = new MessageSink(this);
$(() => {
$(document)
.on('keydown', (e) => this.handleKeyPress(e))
.on('mousedown', (e) => this.clear(e))
.on('mouseenter', '.action-item', (e) => this.showActionInfo($(e.target)))
.on('mouseleave', '.action-item', (e) => this.emptyInfo())
.on('mouseleave', '.action-item', (e) => this.hideActionInfo())
.on('mousemove', (e) => this.mouseInfo = e)
.on('click', '.action-item', (e) => this.handleActionClick(e));
});
}
InputManager.prototype.handleKeyPress = function(e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 27 : this.clear(); break;
}
@ -44,7 +47,7 @@ InputManager.prototype.clear = function(e) {
this.openMenus = [];
}
this.requestedActionInfo = null;
$('#message-sink').hide();
this.messageSink.hide();
};
InputManager.prototype.handleActionClick = function(event) {
@ -62,39 +65,21 @@ InputManager.prototype.registerOpenMenu = function(menu) {
this.openMenus.push(menu);
};
InputManager.messageSink = function() {
return $('#message-sink');
};
InputManager.prototype.emptyInfo = function() {
InputManager.prototype.hideActionInfo = function() {
this.requestedActionInfo = null;
var messageSink = InputManager.messageSink();
messageSink.empty();
messageSink.hide();
this.messageSink.hide();
};
InputManager.prototype.showActionInfo = function(el) {
//show hint immediately and deffer showing the full info
var hint = el.data('actionHint');
if (hint) {
InputManager.messageSink().text(hint);
this.showMessageSinkAround();
}
//var hint = el.data('actionHint');
//if (hint) {
// InputManager.messageSink().text(hint);
// this.showMessageSinkAround();
//}
this.requestInfo(el.data('action'));
};
InputManager.prototype.info = function(text) {
InputManager.messageSink().html(text);
this.showMessageSinkAround();
};
InputManager.prototype.showMessageSinkAround = function() {
var messageSink = InputManager.messageSink();
messageSink.show();
messageSink.offset({left: this.mouseInfo.pageX + 10, top: this.mouseInfo.pageY + 10});
fit(messageSink, $('body'));
};
InputManager.prototype.requestInfo = function(action) {
this.requestedActionInfo = action;
setTimeout(() => {
@ -103,13 +88,14 @@ InputManager.prototype.requestInfo = function(action) {
if (actionId != null) {
const action = this.app.actionManager.actions[actionId];
if (action) {
var hotkey = this.keymap[actionId];
InputManager.messageSink().html(
(action.state.hint ? action.state.hint : '') +
('<div>' + action.info + '</div>') +
(hotkey ? '<div >hotkey: ' + hotkey + '</div>' : ''));
this.showMessageSinkAround();
var hotKey = this.keymap[actionId];
Bind(this.actionInfoDom, {
hint: action.state.hint,
info: action.info,
hotKey: hotKey
});
this.messageSink.showContent(this.actionInfoDom);
}
}
}, 1000);
}, 500);
};

View file

@ -0,0 +1,29 @@
import {fit} from './utils'
export function MessageSink(inputManager) {
this.inputManager = inputManager;
this.node = $('<div>', {'class': 'message-sink'});
$('body').append(this.node);
}
MessageSink.prototype.show = function() {
this.node.show();
this.node.offset({left: this.inputManager.mouseInfo.pageX + 10, top: this.inputManager.mouseInfo.pageY + 10});
fit(this.node, $('body'));
};
MessageSink.prototype.hide = function() {
this.node.hide();
};
MessageSink.prototype.showContent = function(dom) {
this.node.children().detach();
this.node.append(dom);
this.show();
};
MessageSink.prototype.info = function(text) {
this.node.children().detach();
this.node.html(text);
this.show();
};

View file

@ -0,0 +1,5 @@
<div class="action-info">
<div class="action-info-hint" data-bind="hint"></div>
<div class="action-info-info" data-bind="info"></div>
<div class="action-info-hotkey" data-bind="hotKey" bind-format="hotkey: %s"></div>
</div>

View file

@ -70,5 +70,8 @@ export function fit(el, relativeEl) {
top: off.top + 'px'
});
}
}
}
export function LoadTemplate(name) {
return require('./tmpl/' + name + '.html');
}

View file

@ -9,6 +9,8 @@
@control-button-border: 1px solid #2c2c2c;
@menu-border-radius: 3px;
@suppressed-color: #888;
.no-selection {
user-select: none;
-webkit-user-select: none;
@ -187,13 +189,13 @@ body {
}
.menu-item.action-disabled {
color: #888;
color: @suppressed-color;
}
.menu-item .action-hotkey-info {
float: right;
padding-left: 15px;
color: #888;
color: @suppressed-color;
font-size: 9px;
margin-top: 1px;
}
@ -206,16 +208,31 @@ body {
padding-left: 25px;
}
#message-sink {
.message-sink {
display: none;
position: absolute;
max-width: 400px;
padding: 5px;
padding: 2px 5px 2px 5px;
.aux-win;
color: #ccc;
white-space: nowrap;
z-index: 999;
}
.action-info > div {
padding: 3px 0 3px 0;
}
.action-info-hotkey {
text-align: right;
font-style: italic;
color: @suppressed-color;
}
.action-info-hint {
text-align: right;
font-style: italic;
color: #E1A4A4;
}

View file

@ -37,7 +37,6 @@
</div>
</div>
<div id="tab-switcher"></div>
<div id="message-sink"></div>
<a id="downloader" style="display: none;" ></a>
</body>
</html>

View file

@ -28,6 +28,10 @@ module.exports = {
{
test: /\.less$/,
loader: "style!css!less"
},
{
test: /\.html$/,
loader: 'mustache'
}]
}
};