This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Twijn's profile picture

How to re-create the previous UI

Started by Twijn, 10 December 2015 - 02:20 AM
Twijn #1
Posted 10 December 2015 - 03:20 AM
So, what I'm trying to do, is have a program run in the background and when something happens (I already have this part figured out completely) it will create a warning on the top of the screen. Then, a few seconds later, it would remove the message and restore the screen that it was at. I have tried using the window API, assuming that it would – once not visible – would do this, however it seems that that is not the case. Instead it just sits there.

I also thought of keeping a complete "log" of all of the functions ran and then running them all at once when you want to restore them, but that seems highly inefficient to me…
Dragon53535 #2
Posted 10 December 2015 - 03:43 AM
Show the code you're using to handle this, and we can see what you're attempting.
Twijn #3
Posted 10 December 2015 - 03:54 AM
I have this running on startup:

local Collect = true
local ScreenSaver = {}
sapis = {}
function sapis.toggleSS(bool)
  Collect = bool or not Collect
end
local oldC = term.clear
function term.clear(...)
  if Collect then
	ScreenSaver = {}
  end
  return oldC(unpack(args))
end
for i,v in pairs(term)do
  if i:lower() ~= "clear" then
	local oldFunc = v
	term[i] = function(...)
	  if Collect then
		local tbl = {
		  ["Function"] = oldFunc;
		  ["Arguments"] = args;
		}
		table.insert(ScreenSaver,tbl)
	  end
	  return oldFunc(unpack(args))
	end
  end
end
for i,v in pairs(paintutils)do
  if i:lower() ~= "loadimage" then
	local oldFunc = v
	term[i] = function(...)
	  if Collect then
		local tbl = {
		  ["Function"] = oldFunc;
		  ["Arguments"] = args;
		}
		table.insert(ScreenSaver,tbl)
	  end
	  return oldFunc(unpack(args))
	end
  end
end

And this running to activate the "warning":


local function createWarning(func,args)
  sapis.toggleSS(false)
  local lastBC = term.getBackgroundColor()
  local lastTC = term.getTextColor()
  args = args or "None Specified"
  local run = true
  buttonapis.backupButtons()
  term.setTextColor(colors.white)
  paintutils.drawFilledBox(1,1,w,h,colors.red)
  term.setCursorPos(1,1)
  term.write("An unauthorized program is trying to use "..func..".")
  term.setCursorPos(1,2)
  term.write("Given Arguments: "..args)
  term.setCursorPos(1,3)
  term.write("Allow this?")

  buttonapis.addButton(w-6,2,w-4,3,"yes")
  buttonapis.addButton(w-2,2,w-1,3,"no")

  term.setCursorPos(w-6,3)
  term.write("Yes")
  term.setCursorPos(w-2,3)
  term.write("No")

  bfunctions.yes = function(x,y,func)
	restore()
  end

  bfunctions.no = function(x,y,func)
	restore()
  end

  while run do
	buttonapis.handleEvent()
  end
  term.getBackgroundColor(lastBC)
  term.getTextColor(lastTC)
  sapis.toggleSS(true)
end

After calling the createWarning function, the screen literally goes blank, like it shut off. I also have found you can't terminate the program.

EDIT: It is actually doing that at the startup.
Edited on 10 December 2015 - 03:00 AM
Dragon53535 #4
Posted 10 December 2015 - 04:03 AM
Curious question, why are you reverting a state and not just using another window?
CoderPuppy #5
Posted 10 December 2015 - 04:04 AM
Put the main screen in a window and when you hide the popup call `window.redraw()`.

Example:

It waits 5 seconds then opens a popup then 5 seconds later it closes it. (then exits 5 seconds after that)

local mainCo = coroutine.create(function()
    shell.run("shell")
end)
local mainWin = window.create(term.current(), 1, 1, term.getSize())
local mainFilter

local prev = term.redirect(mainWin)
_, mainFilter = coroutine.resume(mainCo)
term.redirect(prev)

local function resize()
	mainWin.reposition(1, 1, term.getSize())
end

local w, h = term.getSize()

local popup
local popupWidth, popupHeight = 15, 10

local timer = os.startTimer(5)
local stage = 0

while true do
    local ev = {os.pullEventRaw()}

    if ev[1] == 'term_resize' then
        w, h = term.getSize()
        resize()
    elseif ev[1] == 'timer' and ev[2] == timer then
         if stage == 0 then
             popup = window.create(term.current(), math.floor(w/2 - popupWidth/2), math.floor(h/2 - popupHeight/2), popupWidth, popupHeight)
             local prevP = term.redirect(popup)
             term.setBackgroundColor(colors.gray)
             term.clear()
             term.setCursorPos(1, 1)
             print("popup")
             term.redirect(prevP)
             popup.redraw()
             mainWin.restoreCursor()
        elseif stage == 1 then
            popup.setVisible(false)
            mainWin.redraw()
            mainWin.restoreCursor()
        elseif stage == 2 then
            break
        end
        timer = os.startTimer(5)
        stage = stage + 1
   end

   if mainFilter == nil or mainFilter == ev[1] then
       local prevTerm = term.redirect(mainWin)
       _, mainFilter = coroutine.resume(mainCo, unpack(ev))
       term.redirect(prevTerm)
       mainWin.redraw()

       if popup then
           popup.redraw()
           mainWin.restoreCursor()
       end
    end
end
Edited on 10 December 2015 - 03:15 AM
Twijn #6
Posted 12 December 2015 - 12:03 AM
Put the main screen in a window and when you hide the popup call `window.redraw()`.

Example:

It waits 5 seconds then opens a popup then 5 seconds later it closes it. (then exits 5 seconds after that)

local mainCo = coroutine.create(function()
	shell.run("shell")
end)
local mainWin = window.create(term.current(), 1, 1, term.getSize())
local mainFilter

local prev = term.redirect(mainWin)
_, mainFilter = coroutine.resume(mainCo)
term.redirect(prev)

local function resize()
	mainWin.reposition(1, 1, term.getSize())
end

local w, h = term.getSize()

local popup
local popupWidth, popupHeight = 15, 10

local timer = os.startTimer(5)
local stage = 0

while true do
	local ev = {os.pullEventRaw()}

	if ev[1] == 'term_resize' then
		w, h = term.getSize()
		resize()
	elseif ev[1] == 'timer' and ev[2] == timer then
		 if stage == 0 then
			 popup = window.create(term.current(), math.floor(w/2 - popupWidth/2), math.floor(h/2 - popupHeight/2), popupWidth, popupHeight)
			 local prevP = term.redirect(popup)
			 term.setBackgroundColor(colors.gray)
			 term.clear()
			 term.setCursorPos(1, 1)
			 print("popup")
			 term.redirect(prevP)
			 popup.redraw()
			 mainWin.restoreCursor()
		elseif stage == 1 then
			popup.setVisible(false)
			mainWin.redraw()
			mainWin.restoreCursor()
		elseif stage == 2 then
			break
		end
		timer = os.startTimer(5)
		stage = stage + 1
   end

   if mainFilter == nil or mainFilter == ev[1] then
	   local prevTerm = term.redirect(mainWin)
	   _, mainFilter = coroutine.resume(mainCo, unpack(ev))
	   term.redirect(prevTerm)
	   mainWin.redraw()

	   if popup then
		   popup.redraw()
		   mainWin.restoreCursor()
	   end
	end
end
That clears the entire screen… I'd like it to keep the screen behind it, if possible.
Bomb Bloke #7
Posted 12 December 2015 - 02:30 AM
That clears the entire screen…

Erm, no it doesn't? Did you try it? The only clear call I can see in there is aimed at the popup's window, and on testing the code snippet out, I find it works for me.
CoderPuppy #8
Posted 12 December 2015 - 02:39 AM
Yes, it clears everything that's on screen when you start it. But if it was startup (and didn't exit after 15 seconds) it would seem like just a normal shell (except that it would open a popup after 5 seconds). I'm guessing you want this for DevOS which already replaces startup which makes clearing everything that was previously there fine.

That clears the entire screen…

Erm, no it doesn't? Did you try it? The only clear call I can see in there is aimed at the popup's window, and on testing the code snippet out, I find it works for me.
It will clear the screen, since the main window covers the entire screen. But it will just put another shell there so it might not look exactly like it clears the screen.
Twijn #9
Posted 12 December 2015 - 02:35 PM
Yes, it clears everything that's on screen when you start it. But if it was startup (and didn't exit after 15 seconds) it would seem like just a normal shell (except that it would open a popup after 5 seconds). I'm guessing you want this for DevOS which already replaces startup which makes clearing everything that was previously there fine.

That clears the entire screen…

Erm, no it doesn't? Did you try it? The only clear call I can see in there is aimed at the popup's window, and on testing the code snippet out, I find it works for me.
It will clear the screen, since the main window covers the entire screen. But it will just put another shell there so it might not look exactly like it clears the screen.
How, then, would I make the screen stay behind it?
CoderPuppy #10
Posted 12 December 2015 - 11:45 PM
There is no way to get the contents of the screen after it has been written. You need to record it somehow (starting on startup), whether that's redirecting to a window or something else.
Bomb Bloke #11
Posted 12 December 2015 - 11:49 PM
The thing is that in order to do this "properly" you need to have access to the window the script you want to put pop-ups over is running in, and you need to have access to the parent of that window. The only way to be sure you have those two things is to create the script's window yourself and then start that script within it.

If you really don't want to do that (and it beats me as to why you wouldn't), then you can cheat a little: at least, if you're using an advanced computer. Multishell automatically runs shell through a window, and it's a fair bet that the window's parent will be term.native(). That might not be true when your pop-up script runs, but it probably will be.

So the code would go along these lines:

-- Make sure we have multishell:
if not multishell then error("This requires an advanced computer with multishell active.") end

-- Wait until this script is running in the current multishell tab:
while multishell.getCurrent() ~= multishell.getFocus() do sleep(5) end  -- An event for tab switching would be helpful here...

-- Make sure we have a window:
local win = term.current()
if not win.redraw then error("Multishell tab has already been redirected away from its window.") end

-- Make an assumption:
local parent = term.native()

-- Draw a pop-up:
local popup = window.create(parent, 10, 10, 5, 5)
popup.setBackgroundColour(colours.red)
popup.clear()

-- Wait a while:
sleep(5)

-- Revert back to the old screen:
win.redraw()
Edited on 12 December 2015 - 10:51 PM
Twijn #12
Posted 13 December 2015 - 02:37 AM
The thing is that in order to do this "properly" you need to have access to the window the script you want to put pop-ups over is running in, and you need to have access to the parent of that window. The only way to be sure you have those two things is to create the script's window yourself and then start that script within it.

If you really don't want to do that (and it beats me as to why you wouldn't), then you can cheat a little: at least, if you're using an advanced computer. Multishell automatically runs shell through a window, and it's a fair bet that the window's parent will be term.native(). That might not be true when your pop-up script runs, but it probably will be.

So the code would go along these lines:

-- Make sure we have multishell:
if not multishell then error("This requires an advanced computer with multishell active.") end

-- Wait until this script is running in the current multishell tab:
while multishell.getCurrent() ~= multishell.getFocus() do sleep(5) end  -- An event for tab switching would be helpful here...

-- Make sure we have a window:
local win = term.current()
if not win.redraw then error("Multishell tab has already been redirected away from its window.") end

-- Make an assumption:
local parent = term.native()

-- Draw a pop-up:
local popup = window.create(parent, 10, 10, 5, 5)
popup.setBackgroundColour(colours.red)
popup.clear()

-- Wait a while:
sleep(5)

-- Revert back to the old screen:
win.redraw()
That one works! Thank you!