Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting padding on the .xterm element #1208

Merged
merged 12 commits into from
Feb 6, 2018
4 changes: 4 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ <h3>Size</h3>
<label for="rows">Rows</label>
<input type="number" id="rows" />
</div>
<div style="display: inline-block; margin-right: 16px;">
<label for="padding">Padding</label>
<input type="number" id="padding" />
</div>
</div>
</div>
</div>
Expand Down
17 changes: 11 additions & 6 deletions demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,27 @@ var terminalContainer = document.getElementById('terminal-container'),
bellStyle: document.querySelector('#option-bell-style')
},
colsElement = document.getElementById('cols'),
rowsElement = document.getElementById('rows');
rowsElement = document.getElementById('rows'),
paddingElement = document.getElementById('padding');

function setTerminalSize() {
var cols = parseInt(colsElement.value, 10);
var rows = parseInt(rowsElement.value, 10);
var viewportElement = document.querySelector('.xterm-viewport');
var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
var width = (cols * term.renderer.dimensions.actualCellWidth + 20 /*room for scrollbar*/).toString() + 'px';
var width = (cols * term.renderer.dimensions.actualCellWidth + term.viewport.scrollBarWidth).toString() + 'px';
var height = (rows * term.renderer.dimensions.actualCellHeight).toString() + 'px';

terminalContainer.style.width = width;
terminalContainer.style.height = height;
term.resize(cols, rows);
term.fit();
}

function setPadding() {
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
term.fit();
}

colsElement.addEventListener('change', setTerminalSize);
rowsElement.addEventListener('change', setTerminalSize);
paddingElement.addEventListener('change', setPadding);

actionElements.findNext.addEventListener('keypress', function (e) {
if (e.key === "Enter") {
Expand Down Expand Up @@ -114,6 +118,7 @@ function createTerminal() {
setTimeout(function () {
colsElement.value = term.cols;
rowsElement.value = term.rows;
paddingElement.value = 0;

// Set terminal size again to set the specific dimensions on the demo
setTerminalSize();
Expand Down
3 changes: 3 additions & 0 deletions src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface ILinkifierAccessor {
}

export interface ITerminal extends ILinkifierAccessor, IBufferAccessor, IElementAccessor, IEventEmitter {
screenElement: HTMLElement;
selectionManager: ISelectionManager;
charMeasure: ICharMeasure;
textarea: HTMLTextAreaElement;
Expand All @@ -48,6 +49,7 @@ export interface ITerminal extends ILinkifierAccessor, IBufferAccessor, IElement
buffers: IBufferSet;
isFocused: boolean;
mouseHelper: IMouseHelper;
viewport: IViewport;
bracketedPasteMode: boolean;

/**
Expand Down Expand Up @@ -184,6 +186,7 @@ export interface IMouseHelper {
}

export interface IViewport {
scrollBarWidth: number;
syncScrollArea(): void;
onWheel(ev: WheelEvent): void;
onTouchStart(ev: TouchEvent): void;
Expand Down
4 changes: 2 additions & 2 deletions src/SelectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
* @param event The mouse event.
*/
private _getMouseBufferCoords(event: MouseEvent): [number, number] {
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.element, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true);
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true);
if (!coords) {
return null;
}
Expand All @@ -290,7 +290,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
* @param event The mouse event.
*/
private _getMouseEventScrollAmount(event: MouseEvent): number {
let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.element)[1];
let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.screenElement)[1];
const terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight);
if (offset >= 0 && offset <= terminalHeight) {
return 0;
Expand Down
9 changes: 7 additions & 2 deletions src/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
export class Terminal extends EventEmitter implements ITerminal, IInputHandlingTerminal {
public textarea: HTMLTextAreaElement;
public element: HTMLElement;
public screenElement: HTMLElement;

/**
* The HTMLElement that the terminal is created in, set by Terminal.open.
Expand Down Expand Up @@ -594,6 +595,10 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.viewportScrollArea.classList.add('xterm-scroll-area');
this.viewportElement.appendChild(this.viewportScrollArea);

this.screenElement = document.createElement('div');
this.screenElement.classList.add('xterm-screen');
fragment.appendChild(this.screenElement);

this._mouseZoneManager = new MouseZoneManager(this);
this.on('scroll', () => this._mouseZoneManager.clearAll());
this.linkifier.attachToDom(this._mouseZoneManager);
Expand Down Expand Up @@ -720,7 +725,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
button = getButton(ev);

// get mouse coordinates
pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
if (!pos) return;

sendEvent(button, pos);
Expand All @@ -746,7 +751,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
function sendMove(ev: MouseEvent): void {
let button = pressed;
let pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
let pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows);
if (!pos) return;

// buttons marked as motions
Expand Down
14 changes: 7 additions & 7 deletions src/Viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IColorSet } from './renderer/Interfaces';
* Logic for the virtual scroll bar is included in this object.
*/
export class Viewport implements IViewport {
public scrollBarWidth: number = 0;
private currentRowHeight: number = 0;
private lastRecordedBufferLength: number = 0;
private lastRecordedViewportHeight: number = 0;
Expand All @@ -31,6 +32,10 @@ export class Viewport implements IViewport {
private scrollArea: HTMLElement,
private charMeasure: CharMeasure
) {
// Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.
// Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case,
// therefore we account 15px to make it visible
this.scrollBarWidth = (this.viewportElement.offsetWidth - this.scrollArea.offsetWidth) || 15;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's pull 15 up to the top to a named constant, maybe FALLBACK_SCROLL_BAR_WIDTH?

this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));

// Perform this async to ensure the CharMeasure is ready.
Expand All @@ -48,13 +53,8 @@ export class Viewport implements IViewport {
private refresh(): void {
if (this.charMeasure.height > 0) {
this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;

if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) {
this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight;
this.viewportElement.style.height = this.lastRecordedViewportHeight + 'px';
}

const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength);
this.lastRecordedViewportHeight = this.viewportElement.offsetHeight;
const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength) + (this.lastRecordedViewportHeight - this.terminal.renderer.dimensions.canvasHeight);
if (this.lastRecordedBufferHeight !== newBufferHeight) {
this.lastRecordedBufferHeight = newBufferHeight;
this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px';
Expand Down
15 changes: 10 additions & 5 deletions src/addons/fit/fit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@ export function proposeGeometry(term: Terminal): IGeometry {
}
const parentElementStyle = window.getComputedStyle(term.element.parentElement);
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')) - 17);
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
const elementStyle = window.getComputedStyle(term.element);
const elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom'));
const elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left'));
const elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
const elementPaddingHor = elementPadding.right + elementPadding.left;
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor;
const availableWidth = parentElementWidth - elementPaddingHor - (<any>term).viewport.scrollBarWidth;
const geometry = {
cols: Math.floor(availableWidth / (<any>term).renderer.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / (<any>term).renderer.dimensions.actualCellHeight)
};

return geometry;
}

Expand Down
2 changes: 1 addition & 1 deletion src/input/MouseZoneManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class MouseZoneManager implements IMouseZoneManager {
}

private _findZoneEventAt(e: MouseEvent): IMouseZone {
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows);
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows);
if (!coords) {
return null;
}
Expand Down
12 changes: 8 additions & 4 deletions src/renderer/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ export class Renderer extends EventEmitter implements IRenderer {
this.colorManager.setTheme(theme);
}
this._renderLayers = [
new TextRenderLayer(this._terminal.element, 0, this.colorManager.colors),
new SelectionRenderLayer(this._terminal.element, 1, this.colorManager.colors),
new LinkRenderLayer(this._terminal.element, 2, this.colorManager.colors, this._terminal),
new CursorRenderLayer(this._terminal.element, 3, this.colorManager.colors)
new TextRenderLayer(this._terminal.screenElement, 0, this.colorManager.colors),
new SelectionRenderLayer(this._terminal.screenElement, 1, this.colorManager.colors),
new LinkRenderLayer(this._terminal.screenElement, 2, this.colorManager.colors, this._terminal),
new CursorRenderLayer(this._terminal.screenElement, 3, this.colorManager.colors)
];
this.dimensions = {
scaledCharWidth: null,
Expand Down Expand Up @@ -118,6 +118,10 @@ export class Renderer extends EventEmitter implements IRenderer {
this._terminal.refresh(0, this._terminal.rows - 1);
}

// Resize the screen
this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth + this._terminal.viewport.scrollBarWidth}px`;
this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`;

this.emit('resize', {
width: this.dimensions.canvasWidth,
height: this.dimensions.canvasHeight
Expand Down
3 changes: 3 additions & 0 deletions src/utils/TestUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class MockTerminal implements ITerminal {
isFocused: boolean;
options: ITerminalOptions = {};
element: HTMLElement;
screenElement: HTMLElement;
rowContainer: HTMLElement;
selectionContainer: HTMLElement;
selectionManager: ISelectionManager;
Expand All @@ -33,6 +34,7 @@ export class MockTerminal implements ITerminal {
scrollback: number;
buffers: IBufferSet;
buffer: IBuffer;
viewport: IViewport;
handler(data: string): void {
throw new Error('Method not implemented.');
}
Expand Down Expand Up @@ -244,6 +246,7 @@ export class MockRenderer implements IRenderer {
}

export class MockViewport implements IViewport {
scrollBarWidth: number = 0;
onThemeChanged(colors: IColorSet): void {
throw new Error('Method not implemented.');
}
Expand Down
12 changes: 11 additions & 1 deletion src/xterm.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,19 @@
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}

.xterm .xterm-screen {
position: relative;
pointer-events: none;
}

.xterm canvas {
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
Expand Down
5 changes: 5 additions & 0 deletions typings/xterm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ declare module 'xterm' {
* The element containing the terminal.
*/
element: HTMLElement;

/**
* The element containing the rendered screen content.
*/
screenElement: HTMLElement;

/**
* The textarea that accepts input for the terminal.
Expand Down