fix overscroll behavior in xterm.js terminal and deal with focus in a better way. restore scrollbars for terminals. scroll focused terminals into view.

This commit is contained in:
sawka 2022-08-24 19:00:03 -07:00
parent da710fc4c8
commit 64fc0f335a
4 changed files with 75 additions and 23 deletions

View File

@ -273,6 +273,16 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
</div>
);
}
@boundMethod
clickTermBlock(e : any, handler : string) {
let {sw, line} = this.props;
let model = GlobalModel;
let termWrap = sw.getTermWrap(line.cmdid);
if (termWrap != null) {
termWrap.terminal.focus();
}
}
render() {
let {sw, line, width} = this.props;
@ -325,6 +335,9 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
</div>
</div>
<div className={cn("terminal-wrapper", {"focus": isFocused})} style={{overflowY: "hidden"}}>
<If condition={!isFocused}>
<div className="term-block" onClick={this.clickTermBlock}></div>
</If>
<div className="terminal" id={"term-" + getLineId(line)} data-cmdid={line.cmdid} style={{height: totalHeight}}></div>
<If condition={!termLoaded}><div style={{position: "absolute", top: 60, left: 30}}>(loading)</div></If>
</div>
@ -348,11 +361,11 @@ class Line extends React.Component<{sw : ScreenWindow, line : LineType, width :
}
@mobxReact.observer
class CmdInput extends React.Component<{}, {}> {
class TextAreaInput extends React.Component<{}, {}> {
lastTab : boolean = false;
lastHistoryUpDown : boolean = false;
lastTabCurLine : mobx.IObservableValue<string> = mobx.observable.box(null);
componentDidMount() {
let input = document.getElementById("main-cmd-input");
if (input != null) {
@ -464,6 +477,23 @@ class CmdInput extends React.Component<{}, {}> {
model.submitRawCommand(commandStr, true);
}
render() {
let model = GlobalModel;
let inputModel = model.inputModel;
let curLine = inputModel.getCurLine();
let numLines = curLine.split("\n").length;
let displayLines = numLines;
if (displayLines > 5) {
displayLines = 5;
}
return (
<textarea id="main-cmd-input" rows={displayLines} value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="textarea"></textarea>
);
}
}
@mobxReact.observer
class CmdInput extends React.Component<{}, {}> {
getAfterSlash(s : string) : string {
if (s.startsWith("^/")) {
return s.substr(1);
@ -479,14 +509,8 @@ class CmdInput extends React.Component<{}, {}> {
}
render() {
console.log("render CmdInput");
let model = GlobalModel;
let inputModel = model.inputModel;
let curLine = inputModel.getCurLine();
let numLines = curLine.split("\n").length;
let displayLines = numLines;
if (displayLines > 5) {
displayLines = 5;
}
let win = GlobalModel.getActiveWindow();
let ri : RemoteInstanceType = null;
let rptr : RemotePtrType = null;
@ -494,7 +518,6 @@ class CmdInput extends React.Component<{}, {}> {
ri = win.getCurRemoteInstance();
rptr = win.curRemote.get();
}
console.log("cmd-input remote", ri);
let remote : RemoteType = null;
let remoteState : RemoteStateType = null;
if (ri != null) {
@ -559,7 +582,7 @@ class CmdInput extends React.Component<{}, {}> {
<div className="button is-static">{remoteStr}</div>
</div>
<div className="control cmd-input-control is-expanded">
<textarea id="main-cmd-input" rows={displayLines} value={curLine} onKeyDown={this.onKeyDown} onChange={this.onChange} className="textarea"></textarea>
<TextAreaInput/>
</div>
<div className="control cmd-exec">
<div onClick={this.doSubmitCmd} className="button">
@ -601,7 +624,7 @@ class ScreenWindowView extends React.Component<{sw : ScreenWindow}, {}> {
if (sw && sw.shouldFollow.get() != atBottom) {
mobx.action(() => sw.shouldFollow.set(atBottom))();
}
// console.log("scroll-handler>", atBottom, target.scrollTop, target.scrollHeight);
// console.log("scroll-handler (sw)>", atBottom, target.scrollTop, target.scrollHeight, event);
}
componentDidMount() {

View File

@ -885,10 +885,6 @@ class Model {
return;
}
termWrap.terminal.focus();
let lineElem = document.getElementById("line-" + getLineId(switchLine));
if (lineElem != null) {
lineElem.scrollIntoView({block: "nearest"});
}
console.log("arrow-up", this.getFocusedLine(), "=>", switchLine);
}
@ -995,6 +991,9 @@ class Model {
this.updateCmd(lineMsg.cmd);
}
}
else if ("cmd" in update) {
this.updateCmd(update.cmd);
}
if ("window" in update) {
let winMsg : WindowUpdateType = update;
this.updateWindow(winMsg.window, false);

View File

@ -313,6 +313,7 @@ html, body, #main {
.line.line-cmd {
flex-direction: column;
scroll-margin-bottom: 20px;
.avatar {
cursor: pointer;
@ -343,10 +344,7 @@ html, body, #main {
align-self: flex-start;
&.focus {
/* box-shadow: -8px 0 12px -5px #aaa; */
/* box-shadow: 0 0 0 4px hsl(0, 0%, 20%);*/
/* filter:drop-shadow(3px 3px 10px #555); */
box-shadow: 0 0 4px 4px rgba(255, 255, 255, 0.3);
box-shadow: 0 0 3px 3px rgba(255, 255, 255, 0.3);
}
}
}
@ -470,6 +468,33 @@ html, body, #main {
body .xterm .xterm-viewport {
overflow-y: auto;
width: calc(100% + 5px);
}
.terminal-wrapper {
position: relative;
.term-block {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: transparent;
z-index: 10;
/* background-color: rgba(50, 50, 240, 0.05); */
}
}
.terminal-wrapper.focus .xterm {
.xterm-screen {
overflow-y: scroll;
overscroll-behavior: contain;
}
.xterm-viewport {
overscroll-behavior: contain;
}
}
.cmd-input-info, .xterm-viewport {
@ -489,7 +514,7 @@ body .xterm .xterm-viewport {
}
.lines {
background-color: black;
/* background-color: rgba(50, 50, 240, 0.05); */
display: flex;
flex-direction: column;
height: 80vh;

View File

@ -64,7 +64,7 @@ class TermWrap {
this.usedRows = mobx.observable.box(termOpts.rows);
}
let cols = termOpts.cols;
let maxCols = Math.trunc((winSize.width - 25) / DefaultCellWidth);
let maxCols = Math.trunc((winSize.width - 25) / DefaultCellWidth) - 1;
if (maxCols > cols) {
cols = maxCols;
}
@ -105,6 +105,12 @@ class TermWrap {
mobx.action(() => {
this.isFocused.set(focus);
})();
if (this.connectedElem != null) {
let lineElem = this.connectedElem.closest(".line");
if (lineElem != null) {
lineElem.scrollIntoView({behavior: "smooth", block: "nearest"});
}
}
}
getTermUsedRows() : number {
@ -200,7 +206,6 @@ class TermWrap {
this.dataUpdates.push({data: data, pos: pos});
return;
}
console.log("pty-update", this.cmdId, this.ptyPos, data.length);
if (pos > this.ptyPos) {
throw new Error(sprintf("invalid pty-update, data-pos[%d] does not match term-pos[%d] cmdid[%s]", pos, this.ptyPos, this.cmdId));
}