blob: c2b0f8a62c58b6f06b39fea5609c37257304dc94 [file] [log] [blame] [raw]
// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import EventEmitter from 'events';
import {options} from '../options';
import {Settings} from '../settings';
import {editor} from 'monaco-editor';
import IEditor = editor.IEditor;
import {FontScaleState} from './fontscale.interfaces';
function getDefaultFontScale() {
return Settings.getStoredSettings().defaultFontScale ?? options.defaultFontScale;
}
function makeFontSizeDropdown(elem: JQuery, obj: FontScale, buttonDropdown: JQuery) {
function onClickEvent(this: JQuery) {
// Toggle off the selection of the others
$(this).addClass('active').siblings().removeClass('active');
// Send the data
obj.applyScale($(this).data('value'));
obj.emit('change');
}
for (let i = 8; i <= 30; i++) {
const item = $('<button></button>');
item.attr('data-value', i)
.addClass('dropdown-item btn btn-sm btn-light')
.text(i)
.appendTo(elem)
.on('click', onClickEvent);
if (obj.scale === i) {
item.addClass('active');
}
}
buttonDropdown.on('wheel', (e: any) => {
e.preventDefault();
let selectedId = elem.find('.active').index();
if (e.originalEvent.deltaY >= 0 && selectedId < elem.children().length - 1) {
selectedId++;
} else if (e.originalEvent.deltaY < 0 && selectedId > 0) {
selectedId--;
}
elem.children().eq(selectedId).trigger('click');
});
// ctrl+click handler
buttonDropdown.on('click', e => {
if (e.ctrlKey) {
// This is a hack. It prevents bootstrap's click listener from opening the dropdown
e.stopImmediatePropagation();
// Set the correct scale as active
elem.find('.active').removeClass('active');
elem.find(`[data-value=${getDefaultFontScale()}]`).addClass('active');
// Set the scale
obj.applyScale(getDefaultFontScale());
obj.emit('change');
}
});
}
function convertOldScale(oldScale: number): number {
// New low + ((new max - new low) * (oldScale - old low) / (old max - old low))
return Math.floor(8 + (22 * (oldScale - 0.3)) / 2.7);
}
export class FontScale extends EventEmitter.EventEmitter {
private domRoot: JQuery;
private fontSizeList: JQuery;
public scale: number;
private readonly usePxUnits: boolean;
private fontSelectorOrEditor: JQuery | string | IEditor;
private isFontOfStr: boolean;
constructor(domRoot: JQuery, state: FontScaleState & any, fontSelectorOrEditor: JQuery | string | IEditor) {
super();
this.domRoot = domRoot;
// Old scale went from 0.3 to 3. New one uses 8 up to 30, so we can convert the old ones to the new format
this.scale = state.fontScale || getDefaultFontScale();
// The check seems pointless, but it ensures a false in case it's undefined
// FontScale assumes it's an old state if it does not see a fontUsePx in the state, so at first it will use pt.
// So the second condition is there to make new objects actually use px
this.usePxUnits = state.fontUsePx === true || !state.fontScale;
if (this.scale < 8) {
this.scale = convertOldScale(this.scale);
}
this.setTarget(fontSelectorOrEditor);
this.apply();
this.fontSizeList = this.domRoot.find('.font-size-list');
makeFontSizeDropdown(this.fontSizeList, this, this.domRoot.find('.fs-button'));
}
apply() {
if (this.isFontOfStr) {
this.domRoot
.find(this.fontSelectorOrEditor as JQuery)
.css('font-size', this.scale + (this.usePxUnits ? 'px' : 'pt'));
} else {
(this.fontSelectorOrEditor as IEditor).updateOptions({
fontSize: this.scale,
});
}
}
addState(state: any) {
state.fontScale = this.scale;
state.fontUsePx = true;
}
applyScale(scale: number) {
this.scale = scale;
this.apply();
}
setScale(scale: number) {
this.fontSizeList.find('.active').removeClass('active');
this.fontSizeList.find(`[data-value=${scale}]`).addClass('active');
this.applyScale(scale);
}
setTarget(target: JQuery | string | IEditor) {
this.fontSelectorOrEditor = target;
this.isFontOfStr = typeof this.fontSelectorOrEditor === 'string';
}
}