mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Desktop: Allow resizing left panels
This commit is contained in:
parent
88e6315d09
commit
81e4cd319d
@ -17,6 +17,7 @@ const { _ } = require('lib/locale.js');
|
||||
const layoutUtils = require('lib/layout-utils.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const eventManager = require('../eventManager');
|
||||
const VerticalResizer = require('./VerticalResizer.min');
|
||||
|
||||
class MainScreenComponent extends React.Component {
|
||||
|
||||
@ -24,6 +25,16 @@ class MainScreenComponent extends React.Component {
|
||||
super();
|
||||
|
||||
this.notePropertiesDialog_close = this.notePropertiesDialog_close.bind(this);
|
||||
this.sidebar_onDrag = this.sidebar_onDrag.bind(this);
|
||||
this.noteList_onDrag = this.noteList_onDrag.bind(this);
|
||||
}
|
||||
|
||||
sidebar_onDrag(event) {
|
||||
Setting.setValue('style.sidebar.width', this.props.sidebarWidth + event.deltaX);
|
||||
}
|
||||
|
||||
noteList_onDrag(event) {
|
||||
Setting.setValue('style.noteList.width', Setting.value('style.noteList.width') + event.deltaX);
|
||||
}
|
||||
|
||||
notePropertiesDialog_close() {
|
||||
@ -265,8 +276,8 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
styles(themeId, width, height, messageBoxVisible, isSidebarVisible) {
|
||||
const styleKey = themeId + '_' + width + '_' + height + '_' + messageBoxVisible + '_' + (+isSidebarVisible);
|
||||
styles(themeId, width, height, messageBoxVisible, isSidebarVisible, sidebarWidth, noteListWidth) {
|
||||
const styleKey = [themeId, width, height, messageBoxVisible, (+isSidebarVisible), sidebarWidth, noteListWidth].join('_');
|
||||
if (styleKey === this.styleKey_) return this.styles_;
|
||||
|
||||
const theme = themeStyle(themeId);
|
||||
@ -288,10 +299,16 @@ class MainScreenComponent extends React.Component {
|
||||
backgroundColor: theme.warningBackgroundColor,
|
||||
}
|
||||
|
||||
this.styles_.verticalResizer = {
|
||||
width: 5,
|
||||
height: height,
|
||||
display: 'inline-block',
|
||||
};
|
||||
|
||||
const rowHeight = height - theme.headerHeight - (messageBoxVisible ? this.styles_.messageBox.height : 0);
|
||||
|
||||
this.styles_.sideBar = {
|
||||
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
|
||||
width: sidebarWidth - this.styles_.verticalResizer.width,
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
@ -303,14 +320,14 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
this.styles_.noteList = {
|
||||
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
|
||||
width: noteListWidth - this.styles_.verticalResizer.width,
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
this.styles_.noteText = {
|
||||
width: Math.floor(layoutUtils.size(width - this.styles_.sideBar.width - this.styles_.noteList.width, 0)),
|
||||
width: Math.floor(width - this.styles_.sideBar.width - this.styles_.noteList.width - 10),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
@ -346,7 +363,7 @@ class MainScreenComponent extends React.Component {
|
||||
const notes = this.props.notes;
|
||||
const messageBoxVisible = this.props.hasDisabledSyncItems || this.props.showMissingMasterKeyMessage;
|
||||
const sidebarVisibility = this.props.sidebarVisibility;
|
||||
const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible, sidebarVisibility);
|
||||
const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible, sidebarVisibility, this.props.sidebarWidth, this.props.noteListWidth);
|
||||
const selectedFolderId = this.props.selectedFolderId;
|
||||
const onConflictFolder = this.props.selectedFolderId === Folder.conflictFolderId();
|
||||
|
||||
@ -462,7 +479,9 @@ class MainScreenComponent extends React.Component {
|
||||
<Header style={styles.header} showBackButton={false} items={headerItems} />
|
||||
{messageComp}
|
||||
<SideBar style={styles.sideBar} />
|
||||
<VerticalResizer style={styles.verticalResizer} onDrag={this.sidebar_onDrag}/>
|
||||
<NoteList style={styles.noteList} />
|
||||
<VerticalResizer style={styles.verticalResizer} onDrag={this.noteList_onDrag}/>
|
||||
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} />
|
||||
</div>
|
||||
);
|
||||
@ -482,6 +501,8 @@ const mapStateToProps = (state) => {
|
||||
showMissingMasterKeyMessage: state.notLoadedMasterKeys.length && state.masterKeys.length,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
sidebarVisibility: state.sidebarVisibility,
|
||||
sidebarWidth: state.settings['style.sidebar.width'],
|
||||
noteListWidth: state.settings['style.noteList.width'],
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,7 @@ class NavigatorComponent extends Component {
|
||||
}
|
||||
|
||||
updateWindowTitle(title) {
|
||||
bridge().window().setTitle(title);
|
||||
if (bridge().window()) bridge().window().setTitle(title);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -18,6 +18,7 @@ const Mark = require('mark.js/dist/mark.min.js');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
const NoteListUtils = require('./utils/NoteListUtils');
|
||||
const { replaceRegexDiacritics, pregQuote } = require('lib/string-utils');
|
||||
const VerticalResizer = require("./VerticalResizer.min");
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
|
||||
@ -29,6 +30,7 @@ class NoteListComponent extends React.Component {
|
||||
|
||||
this.itemRenderer = this.itemRenderer.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.verticalResizer_onDrag = this.verticalResizer_onDrag.bind(this);
|
||||
}
|
||||
|
||||
style() {
|
||||
@ -72,6 +74,10 @@ class NoteListComponent extends React.Component {
|
||||
return style;
|
||||
}
|
||||
|
||||
verticalResizer_onDrag(event) {
|
||||
Setting.setValue('style.noteList.width', Setting.value('style.noteList.width') + event.deltaX);
|
||||
}
|
||||
|
||||
itemContextMenu(event) {
|
||||
const currentItemId = event.currentTarget.getAttribute('data-id');
|
||||
if (!currentItemId) return;
|
||||
@ -381,7 +387,7 @@ class NoteListComponent extends React.Component {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
let notes = this.props.notes.slice();
|
||||
|
||||
|
||||
if (!notes.length) {
|
||||
const padding = 10;
|
||||
const emptyDivStyle = Object.assign({
|
||||
@ -396,16 +402,16 @@ class NoteListComponent extends React.Component {
|
||||
return <div style={emptyDivStyle}>{ this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
return (
|
||||
<ItemList
|
||||
ref={this.itemListRef}
|
||||
itemHeight={this.style().listItem.height}
|
||||
style={style}
|
||||
className={"note-list"}
|
||||
items={notes}
|
||||
style={style}
|
||||
itemRenderer={this.itemRenderer}
|
||||
onKeyDown={this.onKeyDown}
|
||||
></ItemList>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -728,7 +728,8 @@ class SideBarComponent extends React.Component {
|
||||
|
||||
return (
|
||||
<div ref={this.rootRef} onKeyDown={this.onKeyDown} className="side-bar" style={style}>
|
||||
<div style={{flex:1, overflowY: 'auto'}}>
|
||||
|
||||
<div style={{flex:1, overflowX: 'hidden', overflowY: 'auto'}}>
|
||||
{items}
|
||||
</div>
|
||||
<div style={{flex:0}}>
|
||||
|
91
ElectronClient/app/gui/VerticalResizer.jsx
Normal file
91
ElectronClient/app/gui/VerticalResizer.jsx
Normal file
@ -0,0 +1,91 @@
|
||||
const React = require("react");
|
||||
|
||||
class VerticalResizer extends React.PureComponent {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
parentRight: 0,
|
||||
parentHeight: 0,
|
||||
parentWidth: 0,
|
||||
drag: {
|
||||
startX: 0,
|
||||
lastX: 0,
|
||||
},
|
||||
};
|
||||
|
||||
this.onDragStart = this.onDragStart.bind(this);
|
||||
this.onDrag = this.onDrag.bind(this);
|
||||
this.onDragEnd = this.onDragEnd.bind(this);
|
||||
this.document_onDragOver = this.document_onDragOver.bind(this);
|
||||
}
|
||||
|
||||
document_onDragOver(event) {
|
||||
event.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
onDragStart(event) {
|
||||
document.addEventListener('dragover', this.document_onDragOver)
|
||||
|
||||
event.dataTransfer.dropEffect= 'none';
|
||||
|
||||
this.setState({
|
||||
drag: {
|
||||
startX: event.nativeEvent.screenX,
|
||||
lastX: event.nativeEvent.screenX,
|
||||
}
|
||||
});
|
||||
|
||||
if (this.props.onDragStart) this.props.onDragStart({});
|
||||
}
|
||||
|
||||
onDrag(event) {
|
||||
if (!event.nativeEvent.buttons) return;
|
||||
|
||||
const newX = event.nativeEvent.screenX;
|
||||
const delta = newX - this.state.drag.lastX;
|
||||
if (!delta) return;
|
||||
|
||||
this.setState({
|
||||
drag: Object.assign({}, this.state.drag, { lastX: newX }),
|
||||
}, () => {
|
||||
this.props.onDrag({ deltaX: delta });
|
||||
});
|
||||
}
|
||||
|
||||
onDragEnd(event) {
|
||||
document.removeEventListener('dragover', this.document_onDragOver);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('dragover', this.document_onDragOver);
|
||||
}
|
||||
|
||||
render() {
|
||||
const debug = false;
|
||||
|
||||
const rootStyle = Object.assign({}, {
|
||||
height: '100%',
|
||||
width:5,
|
||||
borderColor:'red',
|
||||
borderWidth: debug ? 1 : 0,
|
||||
borderStyle:'solid',
|
||||
cursor: 'col-resize',
|
||||
boxSizing: 'border-box',
|
||||
opacity: 0,
|
||||
}, this.props.style);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={rootStyle}
|
||||
draggable={true}
|
||||
onDragStart={this.onDragStart}
|
||||
onDrag={this.onDrag}
|
||||
onDragEnd={this.onDragEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VerticalResizer;
|
@ -104,6 +104,8 @@ class Setting extends BaseModel {
|
||||
'style.zoom': {value: 100, type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], section: 'appearance', label: () => _('Global zoom percentage'), minimum: 50, maximum: 500, step: 10},
|
||||
'style.editor.fontSize': {value: 13, type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], section: 'appearance', label: () => _('Editor font size'), minimum: 4, maximum: 50, step: 1},
|
||||
'style.editor.fontFamily': {value: "", type: Setting.TYPE_STRING, public: true, appTypes: ['desktop'], section: 'appearance', label: () => _('Editor font family'), description: () => _('This must be *monospace* font or it will not work properly. If the font is incorrect or empty, it will default to a generic monospace font.')},
|
||||
'style.sidebar.width': {value: 150, minimum: 100, maximum: 200, type: Setting.TYPE_INT, public: false, appTypes: ['desktop'] },
|
||||
'style.noteList.width': {value: 150, minimum: 100, maximum: 200, type: Setting.TYPE_INT, public: false, appTypes: ['desktop'] },
|
||||
'autoUpdateEnabled': { value: true, type: Setting.TYPE_BOOL, section:'application', public: true, appTypes: ['desktop'], label: () => _('Automatically update the application') },
|
||||
'autoUpdate.includePreReleases': { value: false, type: Setting.TYPE_BOOL, section:'application', public: true, appTypes: ['desktop'], label: () => _('Get pre-releases when checking for updates'), description: () => _('See the pre-release page for more details: %s', 'https://joplin.cozic.net/prereleases') },
|
||||
'clipperServer.autoStart': { value: false, type: Setting.TYPE_BOOL, public: false },
|
||||
@ -280,6 +282,9 @@ class Setting extends BaseModel {
|
||||
// Don't log this to prevent sensitive info (passwords, auth tokens...) to end up in logs
|
||||
// this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
|
||||
|
||||
if (('minimum' in md) && value < md.minimum) value = md.minimum;
|
||||
if (('maximum' in md) && value > md.maximum) value = md.maximum;
|
||||
|
||||
c.value = value;
|
||||
|
||||
this.dispatch({
|
||||
|
Loading…
Reference in New Issue
Block a user