@ -8,7 +8,8 @@
@@ -8,7 +8,8 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
import QtQuick 2.3
import QtQuick . Controls 1.2
import QtQuick . Controls 1.3
import QtQuick . Controls . Styles 1.4
import QtQuick . Dialogs 1.2
import QtQuick . Layouts 1.2
@ -28,6 +29,10 @@ AnalyzePage {
@@ -28,6 +29,10 @@ AnalyzePage {
property bool isLoaded: false
/ / K e y i n p u t o n m o b i l e i s h a n d l e d d i f f e r e n t l y , s o u s e a s e p a r a t e c o m m a n d i n p u t t e x t f i e l d .
/ / E . g . f o r a n d r o i d s e e h t t p s : / / b u g r e p o r t s . q t . i o / b r o w s e / Q T B U G - 4 0 8 0 3
readonly property bool _separateCommandInput : ScreenTools . isMobile
MavlinkConsoleController {
id: conController
}
@ -44,54 +49,230 @@ AnalyzePage {
@@ -44,54 +49,230 @@ AnalyzePage {
target: conController
onDataChanged: {
/ / K e e p t h e v i e w i n s y n c i f t h e b u t t o n i s c h e c k e d
if ( isLoaded ) {
if ( followTail . checked ) {
listview . positionViewAtEnd ( ) ;
}
/ / r a t e - l i m i t u p d a t e s t o r e d u c e C P U l o a d
updateTimer . start ( ) ;
}
}
}
Component {
id: delegateItem
Rectangle {
color: qgcPal . windowShade
height: Math . round ( ScreenTools . defaultFontPixelHeight * 0.1 + field . height )
width: listview . width
QGCLabel {
id: field
text: display
width: parent . width
wrapMode: Text . NoWrap
font.family: ScreenTools . fixedFontFamily
anchors.verticalCenter: parent . verticalCenter
property int _consoleOutputLen : 0
function scrollToBottom ( ) {
var flickable = textConsole . flickableItem
if ( flickable . contentHeight > flickable . height )
flickable . contentY = flickable . contentHeight - flickable . height
}
function getCommand ( ) {
return textConsole . getText ( _consoleOutputLen , textConsole . length )
}
function getCommandAndClear ( ) {
var command = getCommand ( )
textConsole . remove ( _consoleOutputLen , textConsole . length )
return command
}
function pasteFromClipboard ( ) {
/ / w e n e e d t o h a n d l e a f e w c a s e s h e r e :
/ / i n t h e g e n e r a l f o r m w e h a v e : < c o m m a n d _ p r e > < c u r s o r > < c o m m a n d _ p o s t >
/ / a n d t h e c l i p b o a r d m a y c o n t a i n n e w l i n e s
var cursor = textConsole . cursorPosition - _consoleOutputLen
var command = getCommandAndClear ( )
var command_pre = ""
var command_post = command
if ( cursor > 0 ) {
command_pre = command . substr ( 0 , cursor )
command_post = command . substr ( cursor )
}
var command_leftover = conController . handleClipboard ( command_pre ) + command_post
textConsole . insert ( textConsole . length , command_leftover )
textConsole . cursorPosition = textConsole . length - command_post . length
}
Timer {
id: updateTimer
interval: 30
running: false
repeat: false
onTriggered: {
/ / o n l y u p d a t e i f s c r o l l b a r i s a t t h e b o t t o m
if ( textConsole . flickableItem . atYEnd ) {
/ / b a c k u p & r e s t o r e c u r s o r & c o m m a n d
var command = getCommand ( )
var cursor = textConsole . cursorPosition - _consoleOutputLen
textConsole . text = conController . text
_consoleOutputLen = textConsole . length
textConsole . insert ( textConsole . length , command )
textConsole . cursorPosition = textConsole . length
scrollToBottom ( )
if ( cursor >= 0 ) {
/ / W e c o u l d r e s t o r e t h e s e l e c t i o n h e r e t o o . . .
textConsole . cursorPosition = _consoleOutputLen + cursor
}
} else {
updateTimer . start ( ) ;
}
}
}
QGCListView {
TextArea {
Component.onCompleted: {
isLoaded = true
_consoleOutputLen = textConsole . length
textConsole . cursorPosition = _consoleOutputLen
if ( ! _separateCommandInput )
textConsole . forceActiveFocus ( )
}
id: textConsole
wrapMode: Text . NoWrap
Layout.preferredWidth: parent . width
Layout.fillHeight: true
readOnly: _separateCommandInput
frameVisible: false
textFormat: TextEdit . RichText
inputMethodHints: Qt . ImhNoAutoUppercase | Qt . ImhMultiLine
text: "> "
focus: true
menu: Menu {
id: contextMenu
MenuItem {
text: qsTr ( "Copy" )
onTriggered: {
textConsole . copy ( )
}
}
MenuItem {
text: qsTr ( "Paste" )
onTriggered: {
pasteFromClipboard ( )
}
}
}
style: TextAreaStyle {
textColor: qgcPal . text
backgroundColor: qgcPal . windowShade
selectedTextColor: qgcPal . windowShade
selectionColor: qgcPal . text
font.pointSize: ScreenTools . defaultFontPointSize
font.family: ScreenTools . fixedFontFamily
}
Keys.onPressed: {
if ( event . key == Qt . Key_Tab ) { / / i g n o r e t a b s
event . accepted = true
}
if ( event . matches ( StandardKey . Cut ) ) {
/ / i g n o r e f o r n o w
event . accepted = true
}
if ( ! event . matches ( StandardKey . Copy ) &&
event . key != Qt . Key_Escape &&
event . key != Qt . Key_Insert &&
event . key != Qt . Key_Pause &&
event . key != Qt . Key_Print &&
event . key != Qt . Key_SysReq &&
event . key != Qt . Key_Clear &&
event . key != Qt . Key_Home &&
event . key != Qt . Key_End &&
event . key != Qt . Key_Left &&
event . key != Qt . Key_Up &&
event . key != Qt . Key_Right &&
event . key != Qt . Key_Down &&
event . key != Qt . Key_PageUp &&
event . key != Qt . Key_PageDown &&
event . key != Qt . Key_Shift &&
event . key != Qt . Key_Control &&
event . key != Qt . Key_Meta &&
event . key != Qt . Key_Alt &&
event . key != Qt . Key_AltGr &&
event . key != Qt . Key_CapsLock &&
event . key != Qt . Key_NumLock &&
event . key != Qt . Key_ScrollLock &&
event . key != Qt . Key_Super_L &&
event . key != Qt . Key_Super_R &&
event . key != Qt . Key_Menu &&
event . key != Qt . Key_Hyper_L &&
event . key != Qt . Key_Hyper_R &&
event . key != Qt . Key_Direction_L &&
event . key != Qt . Key_Direction_R ) {
/ / N o t e : d e a d k e y s d o n o t g e n e r a t e k e y P r e s s e d e v e n t o n l i n u x , s e e
/ / h t t p s : / / b u g r e p o r t s . q t . i o / b r o w s e / Q T B U G - 7 9 2 1 6
scrollToBottom ( )
/ / e n s u r e c u r s o r p o s i t i o n i s a t a n e d i t a b l e r e g i o n
if ( textConsole . selectionStart < _consoleOutputLen ) {
textConsole . select ( _consoleOutputLen , textConsole . selectionEnd )
}
if ( textConsole . cursorPosition < _consoleOutputLen ) {
textConsole . cursorPosition = textConsole . length
}
}
if ( event . key == Qt . Key_Left ) {
/ / d o n ' t m o v e b e y o n d c u r r e n t c o m m a n d
if ( textConsole . cursorPosition == _consoleOutputLen ) {
event . accepted = true
}
}
if ( event . key == Qt . Key_Backspace ) {
if ( textConsole . cursorPosition <= _consoleOutputLen ) {
event . accepted = true
}
}
if ( event . key == Qt . Key_Enter || event . key == Qt . Key_Return ) {
conController . sendCommand ( getCommandAndClear ( ) )
event . accepted = true
}
if ( event . matches ( StandardKey . Paste ) ) {
pasteFromClipboard ( )
event . accepted = true
}
/ / c o m m a n d h i s t o r y
if ( event . modifiers == Qt . NoModifier && event . key == Qt . Key_Up ) {
var command = conController . historyUp ( getCommandAndClear ( ) )
textConsole . insert ( textConsole . length , command )
textConsole . cursorPosition = textConsole . length
event . accepted = true
} else if ( event . modifiers == Qt . NoModifier && event . key == Qt . Key_Down ) {
var command = conController . historyDown ( getCommandAndClear ( ) )
textConsole . insert ( textConsole . length , command )
textConsole . cursorPosition = textConsole . length
event . accepted = true
}
}
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
id: listview
model: conController
delegate: delegateItem
/ / U n s y n c t h e v i e w i f t h e u s e r i n t e r a c t s
onMovementStarted: {
followTail . checked = false
MouseArea {
anchors.fill: parent
acceptedButtons: Qt . MiddleButton
onClicked: {
/ / d i s a b l e m i d d l e - c l i c k p a s t i n g ( w e c o u l d a d d s u p p o r t f o r t h a t i f n e e d e d )
}
onWheel: {
/ / i n c r e a s e s c r o l l i n g s p e e d ( t h e d e f a u l t i s a s i n g l e l i n e )
var numLines = 4
var flickable = textConsole . flickableItem
var dy = wheel . angleDelta . y * numLines / 120 * textConsole . font . pixelSize
flickable . contentY = Math . max ( 0 , Math . min ( flickable . contentHeight - flickable . height , flickable . contentY - dy ) )
if ( wheel . angleDelta . x != 0 ) {
var dx = wheel . angleDelta . x * numLines / 120 * textConsole . font . pixelSize
flickable . contentX = Math . max ( 0 , Math . min ( flickable . contentWidth - flickable . width , flickable . contentX - dx ) )
}
wheel . accepted = true
}
}
}
RowLayout {
Layout.fillWidth: true
visible: _separateCommandInput
QGCTextField {
id: command
id: commandInput
Layout.fillWidth: true
placeholderText: "Enter Commands here..."
inputMethodHints: Qt . ImhNoAutoUppercase
@ -99,39 +280,15 @@ AnalyzePage {
@@ -99,39 +280,15 @@ AnalyzePage {
function sendCommand ( ) {
conController . sendCommand ( text )
text = ""
scrollToBottom ( )
}
onAccepted: sendCommand ( )
Keys.onPressed: {
if ( event . key === Qt . Key_Up ) {
text = conController . historyUp ( text ) ;
event . accepted = true ;
} else if ( event . key === Qt . Key_Down ) {
text = conController . historyDown ( text ) ;
event . accepted = true ;
}
}
}
QGCButton {
id: sendButton
text: qsTr ( "Send" )
visible: ScreenTools . isMobile
onClicked: command . sendCommand ( )
}
QGCButton {
id: followTail
text: qsTr ( "Show Latest" )
checkable: true
checked: true
onCheckedChanged: {
if ( checked && isLoaded ) {
listview . positionViewAtEnd ( ) ;
}
}
onClicked: commandInput . sendCommand ( )
}
}
}