mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Terminal plugin (#2646)
This commit is contained in:
@@ -37,6 +37,7 @@ local order = {
|
||||
"storage_stat",
|
||||
"cloud_storage",
|
||||
"read_timer",
|
||||
"terminal",
|
||||
"----------------------------",
|
||||
"advanced_settings",
|
||||
"developer_options",
|
||||
|
||||
@@ -55,6 +55,7 @@ local order = {
|
||||
"synchronize_time",
|
||||
"progress_sync",
|
||||
"zsync",
|
||||
"terminal",
|
||||
},
|
||||
search = {
|
||||
"dictionary_lookup",
|
||||
|
||||
@@ -1,30 +1,56 @@
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local CenterContainer = require("ui/widget/container/centercontainer")
|
||||
local Font = require("ui/font")
|
||||
local Device = require("device")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local Font = require("ui/font")
|
||||
local FrameContainer = require("ui/widget/container/framecontainer")
|
||||
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
||||
local ImageWidget = require("ui/widget/imagewidget")
|
||||
local TextBoxWidget = require("ui/widget/textboxwidget")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Geom = require("ui/geometry")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
local ImageWidget = require("ui/widget/imagewidget")
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local ScrollTextWidget = require("ui/widget/scrolltextwidget")
|
||||
local TextBoxWidget = require("ui/widget/textboxwidget")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local _ = require("gettext")
|
||||
local Input = require("device").input
|
||||
local Screen = require("device").screen
|
||||
local _ = require("gettext")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
|
||||
--[[
|
||||
Widget that displays an informational message
|
||||
|
||||
it vanishes on key press or after a given timeout
|
||||
|
||||
Example:
|
||||
local _ = require("gettext")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local sample
|
||||
sample = InfoMessage:new{
|
||||
text = _("Some message"),
|
||||
-- Usually the hight of a InfoMessage is self-adaptive. If this field is actively set, a
|
||||
-- scrollbar may be shown. This variable is usually helpful to display a large chunk of text
|
||||
-- which may exceed the height of the screen.
|
||||
height = 400,
|
||||
-- Set to false to hide the icon, and also the span between the icon and text.
|
||||
show_icon = false,
|
||||
timeout = 5, -- This widget will vanish in 5 seconds.
|
||||
}
|
||||
sample_input:onShowKeyboard()
|
||||
UIManager:show(sample_input)
|
||||
]]
|
||||
local InfoMessage = InputContainer:new{
|
||||
modal = true,
|
||||
face = Font:getFace("infofont", 25),
|
||||
text = "",
|
||||
timeout = nil, -- in seconds
|
||||
width = nil, -- The width of the InfoMessage. Keep it nil to use default value.
|
||||
height = nil, -- The height of the InfoMessage. If this field is set, a scrollbar may be shown.
|
||||
image = nil, -- The image shows at the left of the InfoMessage.
|
||||
image_width = nil, -- The image width if image is used. Keep it nil to use original width.
|
||||
image_height = nil, -- The image height if image is used. Keep it nil to use original height.
|
||||
-- Whether the icon should be shown. If it is false, self.image will be ignored.
|
||||
show_icon = true,
|
||||
}
|
||||
|
||||
function InfoMessage:init()
|
||||
@@ -46,16 +72,48 @@ function InfoMessage:init()
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local image_widget
|
||||
if self.image then
|
||||
image_widget = ImageWidget:new{
|
||||
image = self.image,
|
||||
width = self.image_width,
|
||||
height = self.image_height,
|
||||
if self.show_icon then
|
||||
if self.image then
|
||||
image_widget = ImageWidget:new{
|
||||
image = self.image,
|
||||
width = self.image_width,
|
||||
height = self.image_height,
|
||||
}
|
||||
else
|
||||
image_widget = ImageWidget:new{
|
||||
file = "resources/info-i.png",
|
||||
}
|
||||
end
|
||||
else
|
||||
image_widget = WidgetContainer:new()
|
||||
end
|
||||
|
||||
local text_width
|
||||
if self.width == nil then
|
||||
text_width = Screen:getWidth() * 2 / 3
|
||||
else
|
||||
text_width = self.width - image_widget:getSize().w
|
||||
if text_width < 0 then
|
||||
text_width = 0
|
||||
end
|
||||
end
|
||||
|
||||
local text_widget
|
||||
if self.height then
|
||||
text_widget = ScrollTextWidget:new{
|
||||
text = self.text,
|
||||
face = self.face,
|
||||
width = text_width,
|
||||
height = self.height,
|
||||
dialog = self,
|
||||
}
|
||||
else
|
||||
image_widget = ImageWidget:new{
|
||||
file = "resources/info-i.png",
|
||||
text_widget = TextBoxWidget:new{
|
||||
text = self.text,
|
||||
face = self.face,
|
||||
width = text_width,
|
||||
}
|
||||
end
|
||||
-- we construct the actual content here because self.text is only available now
|
||||
@@ -67,12 +125,8 @@ function InfoMessage:init()
|
||||
HorizontalGroup:new{
|
||||
align = "center",
|
||||
image_widget,
|
||||
HorizontalSpan:new{ width = 10 },
|
||||
TextBoxWidget:new{
|
||||
text = self.text,
|
||||
face = self.face,
|
||||
width = Screen:getWidth()*2/3,
|
||||
}
|
||||
HorizontalSpan:new{ width = (self.show_icon and 10 or 0) },
|
||||
text_widget,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ function InputText:initTextBox(text, char_added)
|
||||
fgcolor = fgcolor,
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
dialog = self.parent,
|
||||
}
|
||||
else
|
||||
self.text_widget = TextBoxWidget:new{
|
||||
|
||||
@@ -46,7 +46,7 @@ function ScrollTextWidget:init()
|
||||
self.v_scroll_bar = VerticalScrollBar:new{
|
||||
enable = visible_line_count < total_line_count,
|
||||
low = 0,
|
||||
high = visible_line_count/total_line_count,
|
||||
high = visible_line_count / total_line_count,
|
||||
width = self.scroll_bar_width,
|
||||
height = self.height,
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ Example:
|
||||
]]
|
||||
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local Widget = require("ui/widget/widget")
|
||||
local Geom = require("ui/geometry")
|
||||
local LineWidget = require("ui/widget/linewidget")
|
||||
local RenderText = require("ui/rendertext")
|
||||
local Screen = require("device").screen
|
||||
local Geom = require("ui/geometry")
|
||||
local util = require("util")
|
||||
local logger = require("logger")
|
||||
local TimeVal = require("ui/timeval")
|
||||
local Widget = require("ui/widget/widget")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
local TextBoxWidget = Widget:new{
|
||||
text = nil,
|
||||
@@ -225,7 +225,8 @@ function TextBoxWidget:_renderText(start_row_idx, end_row_idx)
|
||||
if start_row_idx < 1 then start_row_idx = 1 end
|
||||
if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end
|
||||
local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1
|
||||
local h = self.line_height_px * row_count
|
||||
local h = self.line_height_px * row_count
|
||||
if self._bb then self._bb:free() end
|
||||
self._bb = Blitbuffer.new(self.width, h)
|
||||
self._bb:fill(Blitbuffer.COLOR_WHITE)
|
||||
local y = font_height
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
local Widget = require("ui/widget/widget")
|
||||
local Geom = require("ui/geometry")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local Geom = require("ui/geometry")
|
||||
local Widget = require("ui/widget/widget")
|
||||
|
||||
local VerticalScrollBar = Widget:new{
|
||||
enable = true,
|
||||
@@ -29,10 +29,10 @@ end
|
||||
function VerticalScrollBar:paintTo(bb, x, y)
|
||||
if not self.enable then return end
|
||||
bb:paintBorder(x, y, self.width, self.height,
|
||||
self.bordersize, self.bordercolor, self.radius)
|
||||
bb:paintRect(x + self.bordersize, y + self.bordersize + self.low*self.height,
|
||||
self.width - 2*self.bordersize,
|
||||
self.height * (self.high - self.low), self.rectcolor)
|
||||
self.bordersize, self.bordercolor, self.radius)
|
||||
bb:paintRect(x + self.bordersize, y + self.bordersize + self.low * self.height,
|
||||
self.width - 2 * self.bordersize,
|
||||
(self.height - 2 * self.bordersize) * (self.high - self.low), self.rectcolor)
|
||||
end
|
||||
|
||||
return VerticalScrollBar
|
||||
|
||||
96
plugins/terminal.koplugin/main.lua
Normal file
96
plugins/terminal.koplugin/main.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
|
||||
local DataStorage = require("datastorage")
|
||||
local Font = require("ui/font")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local InputDialog = require("ui/widget/inputdialog")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local logger = require("logger")
|
||||
local util = require("ffi/util")
|
||||
local _ = require("gettext")
|
||||
local Screen = require("device").screen
|
||||
|
||||
local Terminal = WidgetContainer:new{
|
||||
name = "terminal",
|
||||
dump_file = util.realpath(DataStorage:getDataDir()) .. "/terminal_output.txt",
|
||||
}
|
||||
|
||||
function Terminal:init()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
function Terminal:start()
|
||||
self.input = InputDialog:new{
|
||||
title = _("Enter a command and press \"Execute\""),
|
||||
text_height = Screen:getHeight() * 0.4,
|
||||
input_type = "string",
|
||||
buttons = {{{
|
||||
text = _("Cancel"),
|
||||
callback = function()
|
||||
UIManager:close(self.input)
|
||||
end,
|
||||
}, {
|
||||
text = _("Execute"),
|
||||
is_enter_default = true,
|
||||
callback = function()
|
||||
UIManager:close(self.input)
|
||||
self:execute()
|
||||
end,
|
||||
}}},
|
||||
}
|
||||
self.input:onShowKeyboard()
|
||||
UIManager:show(self.input)
|
||||
end
|
||||
|
||||
function Terminal:execute()
|
||||
local command = self.input:getInputText()
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Executing ..."),
|
||||
timeout = 0.1,
|
||||
})
|
||||
UIManager:forceRePaint()
|
||||
local std_out = io.popen(command)
|
||||
local entries = { command }
|
||||
if std_out then
|
||||
while true do
|
||||
local line = std_out:read()
|
||||
if line == nil then break end
|
||||
table.insert(entries, line)
|
||||
end
|
||||
std_out:close()
|
||||
else
|
||||
table.insert(entries, _("Failed to execute command."))
|
||||
end
|
||||
self:dump(entries)
|
||||
table.insert(entries, _("Output will also be dumped to"))
|
||||
table.insert(entries, self.dump_file)
|
||||
UIManager:show(InfoMessage:new{
|
||||
cface = Font:getFace("ffont", 18),
|
||||
text = _("Command output\n") .. table.concat(entries, "\n"),
|
||||
show_icon = false,
|
||||
width = Screen:getWidth() * 0.8,
|
||||
height = Screen:getHeight() * 0.8,
|
||||
})
|
||||
end
|
||||
|
||||
function Terminal:dump(entries)
|
||||
local content = table.concat(entries, "\n")
|
||||
local file = io.open(self.dump_file, "w")
|
||||
if file then
|
||||
file:write(content)
|
||||
file:close()
|
||||
else
|
||||
logger.warn("Failed to dump terminal output " .. content .. " to " .. self.dump_file)
|
||||
end
|
||||
end
|
||||
|
||||
function Terminal:addToMainMenu(menu_items)
|
||||
menu_items.terminal = {
|
||||
text = _("Terminal emulator"),
|
||||
callback = function()
|
||||
self:start()
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
return Terminal
|
||||
Reference in New Issue
Block a user