From 878ea8958acb6a102751f50c3bbd7389b5195e65 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 21 Nov 2024 21:45:30 +0000 Subject: [PATCH 01/17] [Scripts] Reworking theme, palette and icon management --- data/fonts/options.cfg | 12 +- examples/vue.fluid | 2 +- scripts/gui.fluid | 314 +++++++++++++++++++++---------------- scripts/gui/button.fluid | 22 +-- scripts/gui/checkbox.fluid | 32 ++-- scripts/gui/combobox.fluid | 27 ++-- scripts/gui/fileview.fluid | 7 +- scripts/gui/input.fluid | 27 ++-- scripts/gui/listview.fluid | 75 +++++---- scripts/gui/menubar.fluid | 57 +++---- scripts/gui/toolbar.fluid | 51 +++--- 11 files changed, 343 insertions(+), 283 deletions(-) diff --git a/data/fonts/options.cfg b/data/fonts/options.cfg index 96ce7266d..7e840bcc3 100644 --- a/data/fonts/options.cfg +++ b/data/fonts/options.cfg @@ -16,16 +16,13 @@ Hidden = Yes [Crimson Pro] Hinting = Normal +[Noto Color Emoji] +Hidden = Yes + [Noto Sans] Hinting = Light Default = Yes -[Vollkorn] -Hinting = Normal - -[Noto Color Emoji] -Hidden = Yes - [Source Sans 3] Hidden = Yes @@ -37,3 +34,6 @@ Alias = Source Sans 3 Name = Times New Roman Alias = Crimson Pro Hidden = Yes + +[Vollkorn] +Hinting = Normal diff --git a/examples/vue.fluid b/examples/vue.fluid index cceaf670a..3c003bb56 100644 --- a/examples/vue.fluid +++ b/examples/vue.fluid @@ -138,7 +138,7 @@ function displayImage(Path) glStats = { width = pic.bitmap.width, height = pic.bitmap.height } end, function(Exception) - displayError("

Failed to process selected file.

") + displayError([[

Failed to process selected file.

]]) end) end diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 69ca402e7..2939c5418 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -5,88 +5,122 @@ if (gui == nil) then gui = { theme = 'light', dpi = 160, - fonts = { - -- Use percentages for scalable sizes, where 100% is equivalent to the value of gui.interface.fontSize - default = { face='Noto Sans,Source Sans Pro', size='100%' }, - window = { face='Noto Sans,Source Sans Pro', size='100%' }, -- For text that is not inside a widget - button = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside buttons - icon = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text at the bottom of icons - menu = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Menu item text - titlebar = { face='Noto Sans', size='100%' }, -- Window titlebar text - small = { face='Tiny', size='60%' }, -- Recommended text for small sizes - large = { face='Noto Sans,Source Sans Pro', size='130%' }, -- Recommended text for headers - widget = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside widgets (e.g. combobox) - label = { face='Noto Sans,Source Sans Pro::bold', size='100%' } -- Text for labels and outside widgets - }, - style = { - -- These are all fills, so any SVG compliant fill value can potentially be used to define a 'colour'. - widget = { - text = 'rgb(255,255,255)', - bkgd = 'rgb(0,0,0,100)', - stroke = 'rgb(255,255,255,60)', - strokeFocus = 'rgb(255,255,255,160)', - strokeDisabled = 'rgb(255,255,255,60)', - strokeWidth = 2, - width = 160, -- Default width for common widgets (px) - gap = 6, -- Recommended gap between widgets (px) - margin = 6 -- Recommended internal margin for widgets (px) - }, - button = { - bkgd = 'rgb(230,230,230)', -- Background when raised / default - bkgdActive = 'rgb(200,200,200)' -- Background when temporarily clicked (depressed) - }, - page = { - bkgd = 'rgb(255,255,255)', -- Background for hosting text & paper-based layouts - border = 'rgb(144,144,144)', -- Border stroke for any area surrounding 'bkgd' - borderFocus = 'rgb(160,160,160)', -- Stroke to use when 'border' has the user's focus - text = 'rgb(0,0,0)', -- Default colour for text drawn over 'bkgd' - textHighlight = 'rgb(0,0,0)', -- Colour for 'text' when temporarily highlighted - textHighlightBkgd = 'rgb(224,224,240)' -- Background fill for 'text' that is highlighted - }, - window = { - bkgd = 'rgb(220,220,220)', - text = 'rgb(0,0,0)' -- Colour for text that is drawn directly to the window - }, - menubar = { - bkgd = 'rgb(190,190,190)' - }, - menu = { - bkgd = 'rgb(240,240,240)', - border = 'rgb(200,200,200)' -- Stroke for menu border - }, - desktop = { - bkgd = 'rgb(160,160,160)' - }, - recess = 'rgb(160,160,160)', -- Fill for recessed areas with no content. - strokeShadow = 'rgb(0,0,0,100)', -- Base colour/alpha value for stroked shadows - strokeHighlight = 'rgb(255,255,255,150)' -- Base colour/alpha value for stroked highlights - }, - interface = { - fontSize = 11, -- Default font-size for everything (pt) - iconTheme = 'Default', - iconSize = 20 -- Recommended default size for icons (px) and should be linked to fontSize - }, - iconThemes = { - default = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, - carbon = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, - yellow = { first={ r=0xec,g=0xef,b=0x9e }, last={ r=0xd6,g=0x87,b=0x10 }, stroke={ r=60, g=30, b=30, a=90 } }, - orange = { first={ r=0xFD,g=0xC8,b=0x30 }, last={ r=0xF3,g=0x73,b=0x35 } }, - blue = { first={ r=0x4b,g=0x6c,b=0xb7 }, last={ r=0x18,g=0x28,b=0x48 } }, - ocean = { first={ r=0xA8,g=0xC0,b=0xFF }, last={ r=0x3F,g=0x2B,b=0x96 } }, - green = { first={ r=0x7e,g=0x9d,b=0x13 }, last={ r=0x00,g=0x65,b=0x32 } }, - red = { first={ r=0xf5,g=0xaf,b=0x19 }, last={ r=0xf1,g=0x27,b=0x11 } }, - indigo = { first={ r=0xad,g=0x53,b=0x89 }, last={ r=0x3c,g=0x10,b=0x53 } }, - rose = { first={ r=0xff,g=0xc3,b=0xa0 }, last={ r=0xFF,g=0xAF,b=0xBD } }, - pink = { first={ r=0xff,g=0xdd,b=0xe1 }, last={ r=0xee,g=0x9c,b=0xa7 } }, - grey = { first={ r=100,g=100,b=100 }, last={ r=70,g=70,b=70 } }, - pearl = { first={ r=250,g=249,b=248 }, last={ r=210,g=211,b=212 } } - }, _counter = 0, configureScene = function(Window, Scene) -- A custom configure function can be defined that adds definitions to the window scene, e.g. images and patterns for fills. end } + gui.fonts = { + -- Use percentages for scalable sizes, where 100% is equivalent to the value of gui.interface.fontSize + default = { face='Noto Sans,Source Sans Pro', size='100%' }, + window = { face='Noto Sans,Source Sans Pro', size='100%' }, -- For text that is not inside a widget + button = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside buttons + icon = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text at the bottom of icons + menu = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Menu item text + titlebar = { face='Noto Sans', size='100%' }, -- Window titlebar text + small = { face='Tiny', size='60%' }, -- Recommended text for small sizes + large = { face='Noto Sans,Source Sans Pro', size='130%' }, -- Recommended text for headers + widget = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside widgets (e.g. combobox) + label = { face='Noto Sans,Source Sans Pro::bold', size='100%' } -- Text for labels and outside widgets + } + + gui.iconThemes = { + default = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, + carbon = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, + yellow = { first={ r=0xec,g=0xef,b=0x9e }, last={ r=0xd6,g=0x87,b=0x10 }, stroke={ r=60, g=30, b=30, a=90 } }, + orange = { first={ r=0xFD,g=0xC8,b=0x30 }, last={ r=0xF3,g=0x73,b=0x35 } }, + blue = { first={ r=0x4b,g=0x6c,b=0xb7 }, last={ r=0x18,g=0x28,b=0x48 } }, + ocean = { first={ r=0xA8,g=0xC0,b=0xFF }, last={ r=0x3F,g=0x2B,b=0x96 } }, + green = { first={ r=0x7e,g=0x9d,b=0x13 }, last={ r=0x00,g=0x65,b=0x32 } }, + red = { first={ r=0xf5,g=0xaf,b=0x19 }, last={ r=0xf1,g=0x27,b=0x11 } }, + indigo = { first={ r=0xad,g=0x53,b=0x89 }, last={ r=0x3c,g=0x10,b=0x53 } }, + rose = { first={ r=0xff,g=0xc3,b=0xa0 }, last={ r=0xFF,g=0xAF,b=0xBD } }, + pink = { first={ r=0xff,g=0xdd,b=0xe1 }, last={ r=0xee,g=0x9c,b=0xa7 } }, + grey = { first={ r=100,g=100,b=100 }, last={ r=70,g=70,b=70 } }, + pearl = { first={ r=250,g=249,b=248 }, last={ r=210,g=211,b=212 } } + } + + gui.palettes = { + -- bkgd and invBkgd are light-dark (light theme) or vice versa, and should be of the same hue in the neighbourhood of prime. + -- prime (primary) and second (secondary prime) are colour pairs of the same hue. + -- contrast is a suitable complement to prime and should be of a strongly contrasting hue + -- neutral is a default background colour for unpainted areas, usually a light grey tone + ocean = { type = 'light', bkgd = '#ECF0F1', invBkgd = '#2C3E50', prime = '#3498DB', second = '#2980B9', contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + retroTV = { type = 'light', bkgd = '#f4ebb0', invBkgd = '#6a4654', prime = '#78c2b1', second = '#f45c5d', contrast = '#78c2b1', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + template = { type = '', bkgd = '', invBkgd = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } + } + + gui.palette = gui.palettes.ocean + + gui.style = { + -- All colour values are fills, so gradient and pattern references are also permitted for 'colours'. + widget = { + text = gui.palette.altText, + bkgd = gui.palette.prime, + stroke = 'rgb(255,255,255,60)', + strokeFocus = 'rgb(255,255,255,160)', + strokeDisabled = 'rgb(255,255,255,60)', + strokeWidth = 2, + width = 160, -- Default width for common widgets (px) + gap = 6, -- Recommended gap between widgets (px) + margin = 6 -- Recommended internal margin for widgets (px) + }, + button = { + bkgd = gui.palette.second, -- Background when raised / default + bkgdActive = gui.palette.contrast -- Background when temporarily clicked (depressed) + }, + page = { + bkgd = gui.palette.bkgd, -- Background for hosting text & paper-based layouts + stroke = gui.palette.prime, -- Border stroke for any area surrounding 'bkgd' + strokeFocus = gui.palette.contrast, -- Stroke to use when 'border' has the user's focus + strokeWidth = 2, + text = gui.palette.text, -- Default colour for text drawn over 'bkgd' + icon = '', -- Default icon theme for icons drawn over 'bkgd' + textHighlight = gui.palette.altText, -- Colour for 'text' during user hover + textHighlightBkgd = gui.palette.prime, -- Background fill for 'text' during user hover + iconHighlight = '', -- Recommended icon theme when highlighted + textSelect = gui.palette.altText, -- Colour for 'text' when selected + textSelectBkgd = gui.palette.contrast, -- Background fill for 'text' when selected + iconSelect = '' -- Recommended icon theme when selected + }, + window = { + bkgd = gui.palette.neutral, + text = 'rgb(0,0,0)' -- Colour for text that is drawn directly to the window + }, + menubar = { + bkgd = gui.palette.invBkgd, + text = gui.palette.altText, + highlight = gui.palette.contrast + }, + menu = { + bkgd = gui.palette.bkgd, + border = 'rgb(200,200,200)' -- Stroke for menu border + }, + desktop = { + bkgd = 'rgb(160,160,160)' + } + } + + if gui.palette.type == 'light' then + gui.style.page.icon = 'carbon' + gui.style.page.iconHighlight = 'pearl' + gui.style.page.iconSelect = 'pearl' + gui.style.page.text = gui.palette.text + gui.style.page.textHighlight = gui.palette.altText + gui.style.page.textSelect = gui.palette.altText + else + gui.style.page.icon = 'pearl' + gui.style.page.iconHighlight = 'carbon' + gui.style.page.iconSelect = 'carbon' + end + + gui.interface = { + fontSize = 11, -- Default font-size for everything (pt) + iconTheme = 'Default', -- Named folder to load the icons from + iconSize = 20 -- Recommended default size for icons (px) and should be linked to fontSize + } + function convertFontSizes() -- Convert font percentage sizes to their px size for k,v in pairs(gui.fonts) do if gui.fonts[k].size then @@ -162,7 +196,7 @@ if (gui == nil) then end end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- gui.getFontHeight = function(Font) if not Font.height then @@ -175,7 +209,7 @@ gui.getFontHeight = function(Font) return Font.spacing, Font.height end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Read a style value and return it in pixel units. gui.pixel = function(val) @@ -202,85 +236,93 @@ gui.pixel = function(val) end end ------------------------------------------------------------------------------- --- Convert an RGB string to an RGB table. +---------------------------------------------------------------------------------------------------------------------- +-- Convert an RGB string to an RGB table. Supports '#' and 'rgb()' gui.strToRGB = function(Value) if not Value then error('Empty value passed to strToRGB()\n' .. debug.traceback()) end - if (string.byte(Value) == 35) then - local colour = tonumber(Value) - if (Value:len() == 7) then - return { - r = bit.band(bit.rshift(colour, 16), 0xff), - g = bit.band(bit.rshift(colour, 8), 0xff), - b = bit.band(colour, 0xff), - a = 255 - } - elseif (Value:len() == 9) then - return { - r = bit.band(bit.rshift(colour, 24), 0xff), - g = bit.band(bit.rshift(colour, 16), 0xff), - b = bit.band(bit.rshift(colour, 8), 0xff), - a = bit.band(colour, 0xff) - } + local result + catch(function() + if (string.byte(Value) == 35) then + local colour = tonumber(string.sub(Value,2), 16) + if (Value:len() == 7) then + result = { + r = bit.band(bit.rshift(colour, 16), 0xff), + g = bit.band(bit.rshift(colour, 8), 0xff), + b = bit.band(colour, 0xff), + a = 255 + } + elseif (Value:len() == 9) then + result = { + r = bit.band(bit.rshift(colour, 24), 0xff), + g = bit.band(bit.rshift(colour, 16), 0xff), + b = bit.band(bit.rshift(colour, 8), 0xff), + a = bit.band(colour, 0xff) + } + else + result = { r=0, g=0, b=0, a=255 } + end else - return { r=0, g=0, b=0, a=255 } - end - else - local colours = { } - for str in string.gmatch(Value, '([^,]+)') do - table.insert(colours, tonumber(str)) + local colours = { } + for str in string.gmatch(Value, '([^,]+)') do + table.insert(colours, tonumber(str)) + end + local r = nz(colours[1], 0) + local g = nz(colours[2], 0) + local b = nz(colours[3], 0) + local a = nz(colours[4], 255) + if (r > 255) then r = 255 end + if (g > 255) then g = 255 end + if (b > 255) then b = 255 end + if (a > 255) then a = 255 end + if (r < 0) then r = 0 end + if (g < 0) then g = 0 end + if (b < 0) then b = 0 end + if (a < 0) then a = 0 end + result = { r=r, g=g, b=b, a=a } end - local r = nz(colours[1], 0) - local g = nz(colours[2], 0) - local b = nz(colours[3], 0) - local a = nz(colours[4], 255) - if (r > 255) then r = 255 end - if (g > 255) then g = 255 end - if (b > 255) then b = 255 end - if (a > 255) then a = 255 end - if (r < 0) then r = 0 end - if (g < 0) then g = 0 end - if (b < 0) then b = 0 end - if (a < 0) then a = 0 end - return { r=r, g=g, b=b, a=a } - end + end, + function(Exception) + error(Exception.message .. '\nInput value: ' .. Value .. '\n' .. debug.traceback()) + end) + + return result end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Convert an RGB table to a SVG colour string gui.rgbToSVG = function(Value) return string.format('rgb(%d,%d,%d,%d)', Value.r, Value.g, Value.b, nz(Value.a,255)) end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Interpolate two RGB colours gui.interpolateRGB = function(Base, New) return { - a = Base.a + ((New.a - Base.a)*0.5), - r = Base.r + ((New.r - Base.r)*0.5), - g = Base.g + ((New.g - Base.g)*0.5), - b = Base.b + ((New.b - Base.b)*0.5) + a = Base.a + ((New.a - Base.a) * 0.5), + r = Base.r + ((New.r - Base.r) * 0.5), + g = Base.g + ((New.g - Base.g) * 0.5), + b = Base.b + ((New.b - Base.b) * 0.5) } end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Interpolate two HSV colours gui.interpolateHSV = function(Base, New) return { - a = Base.a + ((New.a - Base.a)*0.5), - h = Base.h + ((New.h - Base.h)*0.5), - s = Base.s + ((New.s - Base.s)*0.5), - v = Base.v + ((New.v - Base.v)*0.5) + a = Base.a + ((New.a - Base.a) * 0.5), + h = Base.h + ((New.h - Base.h) * 0.5), + s = Base.s + ((New.s - Base.s) * 0.5), + v = Base.v + ((New.v - Base.v) * 0.5) } end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Convert HSV table to RGB gui.hsvToRGB = function(Value) @@ -314,17 +356,21 @@ gui.hsvToRGB = function(Value) end end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Return the light value of an RGB colour (i.e. the V in HSV) gui.rgbValue = function(Value) + if not Value then + error('Empty value passed to rgbValue()\n' .. debug.traceback()) + end + local max = Value.b if Value.g > max then max = Value.g end if Value.r > max then max = Value.r end return max end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Convert RGB table to HSV gui.rgbToHSV = function(Value) @@ -361,7 +407,7 @@ gui.rgbToHSV = function(Value) return hsv end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Call a client function for each vector in the tree gui.forVector = function(Vector, Function) @@ -373,7 +419,7 @@ gui.forVector = function(Vector, Function) end end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- gui.applyFill = function(Vector, Fill) while (Vector != nil) do @@ -387,7 +433,7 @@ gui.applyFill = function(Vector, Fill) end end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Generate a simple gradient fill for vectors. gui.simpleGradient = function(Scene, Name, Colours, X1, Y1, X2, Y2, Units, IgnoreClashes) @@ -436,7 +482,7 @@ gui.simpleGradient = function(Scene, Name, Colours, X1, Y1, X2, Y2, Units, Ignor return gradient end ------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------------------------- -- Build an icon styled with the current theme and register it as a pattern. The pattern can then be used as a fill, -- i.e. "url(#Path)". Using it in a VectorRectangle is akin to a cached bitmap (fast), or apply to a VectorViewport -- for live rendering (higher quality transforms). diff --git a/scripts/gui/button.fluid b/scripts/gui/button.fluid index 6a44d41e2..69020487f 100644 --- a/scripts/gui/button.fluid +++ b/scripts/gui/button.fluid @@ -32,7 +32,7 @@ gui.button = function(Options) self.disabled = true self.viewport.acDisable() self.text.opacity = 0.5 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.button.bkgd if self.events.disable then self.events.disable(self) end self.viewport.acDraw() end @@ -41,7 +41,7 @@ gui.button = function(Options) self.disabled = false self.viewport.acDisable() self.text.opacity = 1.0 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.button.bkgd if self.events.enable then self.events.enable(self) end self.viewport.acDraw() end @@ -76,7 +76,7 @@ gui.button = function(Options) self.viewport = self.parentViewport.new('VectorViewport', vps) self.bkgd = self.viewport.new('VectorRectangle', { - fill = gui.style.widget.bkgd, + fill = gui.style.button.bkgd, x = gui.style.widget.strokeWidth * 0.5, y = gui.style.widget.strokeWidth * 0.5, width = self.viewport.width - gui.style.widget.strokeWidth, @@ -113,14 +113,16 @@ gui.button = function(Options) end self._textClip = self.viewport.new('VectorViewport', { - x=gui.style.widget.strokeWidth, y=gui.style.widget.strokeWidth, xOffset=gui.style.widget.strokeWidth, yOffset=gui.style.widget.strokeWidth, overflow=VOF_HIDDEN + x=gui.style.widget.strokeWidth, y=gui.style.widget.strokeWidth, + xOffset=gui.style.widget.strokeWidth, yOffset=gui.style.widget.strokeWidth, + overflow=VOF_HIDDEN }) self.text = self._textClip.new('VectorText', { x = GAP + label_offset, - y = FONT_HEIGHT + math.floor((HEIGHT - FONT_HEIGHT - gui.style.widget.strokeWidth - gui.style.widget.strokeWidth) * 0.5), - face = gui.fonts.widget.face, - fontSize = string.format('%.2fpt', gui.fonts.widget.size), + y = FONT_HEIGHT + math.floor((HEIGHT - FONT_HEIGHT - (gui.style.widget.strokeWidth * 2)) * 0.5), + face = gui.fonts.button.face, + fontSize = string.format('%.2fpt', gui.fonts.button.size), fill = gui.style.widget.text, string = Options.text, lineLimit = 1 @@ -157,14 +159,12 @@ gui.button = function(Options) self.viewport.mtSubscribeInput(bit.bor(JTYPE_CROSSING, JTYPE_BUTTON, JTYPE_REPEATED), function(Viewport, Msg) repeat if (Msg.type == JET_CROSSED_IN) then - local highlight = gui.strToRGB(gui.style.widget.bkgd) - highlight.a = highlight.a * 0.5 - self.bkgd.fill = gui.rgbToSVG(highlight) + self.bkgd.fill = gui.style.button.bkgdActive if self.events.cursorEntry then self.events.cursorEntry(self) end elseif (Msg.type == JET_CROSSED_OUT) then - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.button.bkgd if self.events.cursorExit then self.events.cursorExit(self) end diff --git a/scripts/gui/checkbox.fluid b/scripts/gui/checkbox.fluid index 7528c4247..02d1aed38 100644 --- a/scripts/gui/checkbox.fluid +++ b/scripts/gui/checkbox.fluid @@ -13,7 +13,7 @@ local ICON_SIZE = HEIGHT * 0.56 local GAP = HEIGHT * 0.35 local function iconTheme() - if (gui.rgbValue(gui.strToRGB(gui.style.widget.text)) > 127) then + if (gui.rgbValue(gui.strToRGB(gui.style.page.text)) > 127) then return 'pearl' else return 'default' -- The default theme is dark @@ -47,7 +47,7 @@ gui.checkbox = function(Options) self.disabled = true self.viewport.acDisable() self.text.opacity = 0.5 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.disable then self.events.disable(self) end self.viewport.acDraw() end @@ -56,7 +56,7 @@ gui.checkbox = function(Options) self.disabled = false self.viewport.acDisable() self.text.opacity = 1.0 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.enable then self.events.enable(self) end self.viewport.acDraw() end @@ -88,15 +88,15 @@ gui.checkbox = function(Options) self.viewport = self.parentViewport.new('VectorViewport', vps) self.bkgd = self.viewport.new('VectorRectangle', { - fill = gui.style.widget.bkgd, - x = gui.style.widget.strokeWidth * 0.5, - y = gui.style.widget.strokeWidth * 0.5, - width = HEIGHT - gui.style.widget.strokeWidth, - height = HEIGHT - gui.style.widget.strokeWidth, - roundX = gui.style.widget.strokeWidth * 2, - roundY = gui.style.widget.strokeWidth * 2, - stroke = gui.style.widget.stroke, - strokeWidth = gui.style.widget.strokeWidth, + fill = gui.style.page.bkgd, + x = gui.style.page.strokeWidth * 0.5, + y = gui.style.page.strokeWidth * 0.5, + width = HEIGHT - gui.style.page.strokeWidth, + height = HEIGHT - gui.style.page.strokeWidth, + roundX = gui.style.page.strokeWidth * 2, + roundY = gui.style.page.strokeWidth * 2, + stroke = gui.style.page.stroke, + strokeWidth = gui.style.page.strokeWidth, }) gui.createIcon(self.viewport.scene, 'items/checkmark', ICON_SIZE, iconTheme()) @@ -132,7 +132,7 @@ gui.checkbox = function(Options) if Options.align == 'right' then self.text.x = 0 - self.bkgd.x = self.viewport.width - HEIGHT + (gui.style.widget.strokeWidth * 0.5) + self.bkgd.x = self.viewport.width - HEIGHT + (gui.style.page.strokeWidth * 0.5) self.iconRender.x = self.viewport.width - HEIGHT + ((HEIGHT - ICON_SIZE) * 0.5) else self.text.x = GAP + (GAP * 0.5) + ICON_SIZE @@ -140,13 +140,13 @@ gui.checkbox = function(Options) self.viewport.mtSubscribeFeedback(bit.bor(FM_HAS_FOCUS, FM_CHILD_HAS_FOCUS, FM_LOST_FOCUS), function(Viewport, Event) if (Event == FM_LOST_FOCUS) then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke if self.events.lostFocus then self.events.lostFocus(self) end else if self.disabled then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke else - self.bkgd.stroke = gui.style.widget.strokeFocus + self.bkgd.stroke = gui.style.page.strokeFocus if self.events.focus then self.events.focus(self) end end end diff --git a/scripts/gui/combobox.fluid b/scripts/gui/combobox.fluid index a18726654..7b139739a 100644 --- a/scripts/gui/combobox.fluid +++ b/scripts/gui/combobox.fluid @@ -13,7 +13,7 @@ local BOX_HEIGHT = math.floor(FONT_MAX_HEIGHT * 1.35) + 1 local MARGIN = math.floor(FONT_HEIGHT * 0.5) + 1 local function iconTheme() - if (gui.rgbValue(gui.strToRGB(gui.style.widget.text)) > 127) then + if (gui.rgbValue(gui.strToRGB(gui.style.page.text)) > 127) then return 'pearl' else return 'default' -- The default theme is dark @@ -58,7 +58,7 @@ gui.combobox = function(Options) self.disabled = true self.viewport.acDisable() self.text.opacity = 0.5 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.disable then self.events.disable(self) end self.viewport.acDraw() end @@ -67,7 +67,7 @@ gui.combobox = function(Options) self.disabled = false self.viewport.acDisable() self.text.opacity = 1.0 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.enable then self.events.enable(self) end self.viewport.acDraw() end @@ -132,7 +132,7 @@ gui.combobox = function(Options) end self.bkgd = self.inputViewport.new('VectorRectangle', { - fill = gui.style.widget.bkgd, + fill = gui.style.page.bkgd, x = 0, y = 0, width = '100%', @@ -151,17 +151,18 @@ gui.combobox = function(Options) }) if not Options.flushEdges then - self.bkgd.stroke = gui.style.widget.stroke - self.bkgd.strokeWidth = gui.style.widget.strokeWidth - self.bkgd.roundX = gui.style.widget.strokeWidth * 2 - self.bkgd.roundY = gui.style.widget.strokeWidth * 2 + self.bkgd.stroke = gui.style.page.stroke + self.bkgd.strokeWidth = gui.style.page.strokeWidth + self.bkgd.roundX = gui.style.page.strokeWidth * 2 + self.bkgd.roundY = gui.style.page.strokeWidth * 2 end self._textClip = self.inputViewport.new('VectorViewport', { cursor = (function() if self.allowCustom then return 'text' end end)(), - x=gui.style.widget.strokeWidth, y=gui.style.widget.strokeWidth, xOffset=self.iconRender.width + MARGIN + MARGIN, yOffset=gui.style.widget.strokeWidth, overflow=VOF_HIDDEN + x=gui.style.widget.strokeWidth, y=gui.style.widget.strokeWidth, + xOffset=self.iconRender.width + MARGIN + MARGIN, yOffset=gui.style.widget.strokeWidth, overflow=VOF_HIDDEN }) local defaultText = Options.text @@ -174,7 +175,7 @@ gui.combobox = function(Options) y = FONT_HEIGHT + math.floor((BOX_HEIGHT - FONT_HEIGHT - (gui.style.widget.strokeWidth * 2)) * 0.5), face = gui.fonts.widget.face, fontSize = string.format('%.2fpt', gui.fonts.widget.size), - fill = gui.style.widget.text, + fill = gui.style.page.text, string = defaultText, lineLimit = 1, textFlags = (function() @@ -190,7 +191,7 @@ gui.combobox = function(Options) self.viewport.mtSubscribeFeedback(bit.bor(FM_HAS_FOCUS, FM_CHILD_HAS_FOCUS, FM_LOST_FOCUS), function(Viewport, Event) if (Event == FM_LOST_FOCUS) then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke if self.events.lostFocus then self.events.lostFocus(self) end if self.allowCustom then reportFeedback(self) @@ -200,9 +201,9 @@ gui.combobox = function(Options) else self._lastGoodInput = self.text.string if self.disabled then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke else - self.bkgd.stroke = gui.style.widget.strokeFocus + self.bkgd.stroke = gui.style.page.strokeFocus if self.events.focus then self.events.focus(self) end end end diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index 9d352780d..6fabdef3f 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -1470,9 +1470,14 @@ gui.fileview = function(View, Options) end if Options.navigationBar then - local bkgd = self.view.viewport.new('VectorRectangle', { x=0, y=0, xOffset=0, height=1, fill='rgb(0,0,0,20)' }) + local bkgd = self.view.viewport.new('VectorRectangle', { + x=0, y=0, xOffset=0, height=1, fill='#2C3E50' + }) self.toolbar = gui.toolbar({ + iconTheme = 'pearl', + hoverFill = '#E74C3C', + selectFill = '#2980B9', target = self.view.viewport, x = 0, y = 0, diff --git a/scripts/gui/input.fluid b/scripts/gui/input.fluid index 063656c77..371e711b5 100644 --- a/scripts/gui/input.fluid +++ b/scripts/gui/input.fluid @@ -47,7 +47,7 @@ gui.input = function(Options) self.disabled = true self.viewport.acDisable() self.text.opacity = 0.5 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.disable then self.events.disable(self) end self.viewport.acDraw() end @@ -56,7 +56,7 @@ gui.input = function(Options) self.disabled = false self.viewport.acDisable() self.text.opacity = 1.0 - self.bkgd.fill = gui.style.widget.bkgd + self.bkgd.fill = gui.style.page.bkgd if self.events.enable then self.events.enable(self) end self.viewport.acDraw() end @@ -123,7 +123,7 @@ gui.input = function(Options) end self.bkgd = self.inputViewport.new('VectorRectangle', { - fill = gui.style.widget.bkgd, + fill = gui.style.page.bkgd, x = 0, y = 0, width = '100%', @@ -131,25 +131,26 @@ gui.input = function(Options) }) if not Options.flushEdges then - self.bkgd.stroke = gui.style.widget.stroke - self.bkgd.strokeWidth = gui.style.widget.strokeWidth - self.bkgd.roundX = gui.style.widget.strokeWidth * 2 - self.bkgd.roundY = gui.style.widget.strokeWidth * 2 + self.bkgd.stroke = gui.style.page.stroke + self.bkgd.strokeWidth = gui.style.page.strokeWidth + self.bkgd.roundX = gui.style.page.strokeWidth * 2 + self.bkgd.roundY = gui.style.page.strokeWidth * 2 end self._textClip = self.inputViewport.new('VectorViewport', { name = 'vp_input_clip', - x = gui.style.widget.strokeWidth, y = gui.style.widget.strokeWidth, xOffset = gui.style.widget.strokeWidth, yOffset = gui.style.widget.strokeWidth, + x = gui.style.page.strokeWidth, y = gui.style.page.strokeWidth, + xOffset = gui.style.page.strokeWidth, yOffset = gui.style.page.strokeWidth, overflow = VOF_HIDDEN }) self.text = self._textClip.new('VectorText', { name = 'vt_input', x = 4, - y = font_height + math.floor((box_height - font_height - (gui.style.widget.strokeWidth * 2)) * 0.5), + y = font_height + math.floor((box_height - font_height - (gui.style.page.strokeWidth * 2)) * 0.5), face = gui.fonts.widget.face, fontSize = string.format('%.2fpt', gui.fonts.widget.size), - fill = gui.style.widget.text, + fill = gui.style.page.text, string = Options.text, lineLimit = 1, textFlags = (function() @@ -163,7 +164,7 @@ gui.input = function(Options) self.viewport.mtSubscribeFeedback(bit.bor(FM_HAS_FOCUS, FM_CHILD_HAS_FOCUS, FM_LOST_FOCUS), function(Viewport, Event) if (Event == FM_LOST_FOCUS) then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke if self.events.lostFocus then self.events.lostFocus(self) end if validateInput(self) then reportActivate(self, 'lost-focus') @@ -171,9 +172,9 @@ gui.input = function(Options) else self._lastGoodInput = self.text.string if self.disabled then - self.bkgd.stroke = gui.style.widget.stroke + self.bkgd.stroke = gui.style.page.stroke else - self.bkgd.stroke = gui.style.widget.strokeFocus + self.bkgd.stroke = gui.style.page.strokeFocus if self.events.focus then self.events.focus(self) end end end diff --git a/scripts/gui/listview.fluid b/scripts/gui/listview.fluid index fc3d40da6..e3314e4ca 100644 --- a/scripts/gui/listview.fluid +++ b/scripts/gui/listview.fluid @@ -14,15 +14,6 @@ local LINE_HEIGHT, FONT_HEIGHT = gui.getFontHeight(gui.fonts.widget) LINE_HEIGHT = math.round(LINE_HEIGHT * 1.3) local LINE_OFFSET = (LINE_HEIGHT + FONT_HEIGHT) * 0.5 -local lColours = { - fillItem = gui.style.page.text, - fillItemSelectHigh = 'rgb(255,255,255)', - fillSelect = 'rgb(0,0,60,140)', - fillSelectFont = 'rgb(255,255,255)', - fillBkgdHighlight = 'rgb(0,0,0,10)', - fillBkgdSelectHighlight = 'rgb(0,0,90,140)' -} - local function rearrangeItems(View) View.zones = { } local y = 0 @@ -68,23 +59,26 @@ gui.listView = function(Options) Item._vectors.highlight.acMoveToBack() end - if Highlighted then - if Item._selected then -- Both selected AND highlighted - Item._vectors.highlight.fill = lColours.fillBkgdSelectHighlight - setListTextFill(Item, lColours.fillItemSelectHigh) - else - Item._vectors.highlight.fill = lColours.fillBkgdHighlight - setListTextFill(Item, lColours.fillItem) - end - + if (Item._selected) then + setListTextFill(Item, gui.style.page.textSelect) + Item._vectors.highlight.fill = gui.style.page.textSelectBkgd Item._vectors.highlight.visibility = VIS_VISIBLE - elseif (Item._selected) then - setListTextFill(Item, lColours.fillItemSelectHigh) - Item._vectors.highlight.fill = lColours.fillSelect + if gui.style.page.iconSelect and View.sIcons[Item.icon] then + Item._vectors.iconImage.fill = 'url(#' .. View.sIcons[Item.icon] .. ')' + end + elseif Highlighted then + Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd + setListTextFill(Item, gui.style.page.textHighlight) Item._vectors.highlight.visibility = VIS_VISIBLE + if gui.style.page.iconHighlight and View.hIcons[Item.icon] then + Item._vectors.iconImage.fill = 'url(#' .. View.hIcons[Item.icon] .. ')' + end else - setListTextFill(Item, lColours.fillItem) + setListTextFill(Item, gui.style.page.text) Item._vectors.highlight.visibility = VIS_HIDDEN + if View.icons[Item.icon] then + Item._vectors.iconImage.fill = 'url(#' .. Item.icon .. ')' + end end end @@ -147,13 +141,32 @@ gui.listView = function(Options) if item.icon then pcall(function() - if not View.icons then View.icons = { } end - if not View.icons[item.icon] then - gui.createIcon(View.scene, item.icon, ICON_SIZE) + gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.icon, item.icon) View.icons[item.icon] = true end + if gui.style.page.iconSelect then + if (gui.style.page.iconSelect == gui.style.page.icon) then + View.sIcons[item.icon] = item.icon + else + gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconSelect, '_s_' .. item.icon) + View.sIcons[item.icon] = '_s_' .. item.icon + end + end + + if gui.style.page.iconHighlight then + if (gui.style.page.iconHighlight == gui.style.page.icon) then + View.hIcons[item.icon] = item.icon + elseif (gui.style.page.iconHighlight != gui.style.page.iconSelect) then + gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconHighlight, '_s_' .. item.icon) + View.hIcons[item.icon] = '_s_' .. item.icon + else + gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconHighlight, '_h_' .. item.icon) + View.hIcons[item.icon] = '_h_' .. item.icon + end + end + if not item._vectors.iconImage then item._vectors.iconImage = vp.new('VectorRectangle', { fill = 'url(#' .. item.icon .. ')', @@ -191,11 +204,11 @@ gui.listView = function(Options) width = MARGIN + ICON_MARGIN + bw + MARGIN if item._selected then - item._vectors.text.fill = lColours.fillSelectFont - elseif (item.index == lHighlightedItem) and (lColours.highlight) then - item._vectors.text.fill = lColours.highlight + item._vectors.text.fill = gui.style.page.textSelect + elseif item.index and (item.index == lHighlightedItem) then + item._vectors.text.fill = gui.style.page.textHighlight else - item._vectors.text.fill = nz(item.colour, lColours.fillItem) + item._vectors.text.fill = nz(item.colour, gui.style.page.text) end end @@ -230,6 +243,10 @@ gui.listView = function(Options) end local function constructListView(View, Scene, Window, Page) + if not View.icons then View.icons = { } end + if not View.sIcons then View.sIcons = { } end + if not View.hIcons then View.hIcons = { } end + if #View.items > 0 then newItems(View, View.items) end collectgarbage() diff --git a/scripts/gui/menubar.fluid b/scripts/gui/menubar.fluid index 1ae78481e..3b6d84f28 100644 --- a/scripts/gui/menubar.fluid +++ b/scripts/gui/menubar.fluid @@ -8,12 +8,6 @@ Documentation is available in the Parasol Wiki. require 'translate' include 'vector' -local lColours = { - textFill = 'rgb(255,255,255)', - textHighlight = 'rgb(160,160,220)', - iconGradient = { first={ r=250,g=249,b=248 }, last={ r=190,g=191,b=192 } } -} - local VMARGIN = gui.style.widget.margin local GAP = 26 local ICON_SIZE = 12 @@ -101,11 +95,12 @@ end local function highlightItem(self, Item) self._currentItem = Item - self._hIcon.x = Item.x + 1 - self._hIcon.y = 1 - self._hIcon.width = Item.width - 2 - self._hIcon.height = self.viewport.height - 2 - self._hIcon.visibility = 'visible' + + self._highlight.x = Item.x + self._highlight.y = 0 + self._highlight.width = Item.width + self._highlight.height = self.viewport.height - 1 + self._highlight.visibility = 'visible' self.viewport.acDraw() end @@ -141,7 +136,7 @@ local function arrangeItems(self) x = x + iconWidth + VMARGIN, face = gui.fonts.menu.face, fontSize = string.format('%.2fpt', gui.fonts.menu.size), - fill = lColours.textFill, + fill = gui.style.menubar.text, string = item.displayName }) @@ -247,15 +242,15 @@ gui.menubar = function(Options) local function hoverUpdate(Item, StateChanged) -- Highlight items that the user hovers over. if (self._currentItem == Item) and (StateChanged != true) then return end - self._hIcon.visibility = 'hidden' -- Turn off keyboard highlighting + self._highlight.visibility = 'hidden' self._currentItem = Item if self._prevItem and (self._prevItem != self._currentItem) then - self._prevItem.vectorText.fill = lColours.textFill + -- Reset prevItem presentation if necessary end if self._currentItem and (not self._currentItem.disabled) and (not self._disabled) then - self._currentItem.vectorText.fill = lColours.textHighlight + highlightItem(self, self._currentItem) self._prevItem = self._currentItem end @@ -283,7 +278,7 @@ gui.menubar = function(Options) if (Event == FM_LOST_FOCUS) then self._hasFocus = false if self._currentItem then - self._hIcon.visibility = 'hidden' -- Turn off keyboard highlighting + self._highlight.visibility = 'hidden' -- Turn off highlighting end if self._lastMenu then @@ -369,38 +364,28 @@ gui.menubar = function(Options) self._bkgd = self.viewport.new('VectorGroup', { name='ItemBackground' }) - self._hIcon = self.viewport.new('VectorRectangle', { -- Keyboard-based selection - stroke = 'rgb(0,0,0,128)', - dashArray = '1, 1', - roundX = 3, - roundY = 3, - x = 0, - y = 0, - width = 16, - height = 16, - visibility = 'hidden' - }) - Options.createItems(self) self.viewport.height = ICON_SIZE + (VMARGIN * 2) self._bkgd.new('VectorRectangle', { -- Main background - fill = 'rgb(0,0,0,200)', + fill = gui.style.menubar.bkgd, x = 0, y = 0, width = '100%', height = self.viewport.height - 1 }) + self._highlight = self._bkgd.new('VectorRectangle', { -- Highlight for items + fill = gui.style.menubar.highlight, + x = 0, y = 0, width = 1, height = self.viewport.height - 1, + visibility = 'hidden' + }) + self._bkgd.new('VectorRectangle', { -- Bottom stroke - fill = 'rgb(0,0,0,64)', - stroke = 'rgb(255,255,255,64)', - strokeWidth = 1, - x = 0, - y = self.viewport.height - 1, - width = '100%', - height = 1 + fill = 'rgb(0,0,0,64)', + stroke = 'rgb(255,255,255,64)', strokeWidth = 1, + x = 0, y = self.viewport.height - 1, width = '100%', height = 1 }) arrangeItems(self) diff --git a/scripts/gui/toolbar.fluid b/scripts/gui/toolbar.fluid index 1cb19b28d..0dcbc1570 100644 --- a/scripts/gui/toolbar.fluid +++ b/scripts/gui/toolbar.fluid @@ -8,13 +8,6 @@ Documentation is available in the Parasol Wiki. require 'translate' include 'vector' -local lColours = { - hover = 'rgb(0,0,0,32)', - selected = 'rgb(255,255,255,128)', - groupBorder = 'rgb(255,255,255,128)', - iconGradient = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } } -} - local lCounter = 0 local lTipDelay = 0.75 @@ -38,7 +31,11 @@ gui.toolbar = function(Options) _disabled = false, _lookup = { }, _iconSize = nz(Options.iconSize, gui.interface.iconSize), - _hasFocus = false + _hasFocus = false, + _iconTheme = nz(Options.iconTheme, { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }), + _hoverFill = nz(Options.hoverFill, 'rgb(0,0,0,32)'), + _selectFill = nz(Options.selectFill, 'rgb(255,255,255,128)'), + _groupBorder = nz(Options.groupBorder, 'rgb(255,255,255,128)') } local function sendFeedback() @@ -122,15 +119,17 @@ gui.toolbar = function(Options) groupOffset = self._groupMargin end - if (not self._vHover) then + if (not self._vHover) then -- Hover highlight if (self._groupCount > 0) then - self._vHover = self.viewport.new('VectorRectangle', { -- Hover highlight - fill='rgb(255,255,255)', x=0, y=0, width=1, height=1, visibility='hidden' + self._vHover = self.viewport.new('VectorRectangle', { + fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden' }) + self._vHover.acMoveToBack() else - self._vHover = self.viewport.new('VectorRectangle', { -- Hover highlight - fill='rgb(255,255,255)', x=0, y=0, width=1, height=1, visibility='hidden', roundX=self._round, roundY=self._round + self._vHover = self.viewport.new('VectorRectangle', { + fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden', roundX=self._round, roundY=self._round }) + self._vHover.acMoveToBack() end end @@ -167,7 +166,7 @@ gui.toolbar = function(Options) local seq = string.format('M%f,%f l0,%f', x+(item.width-1) * 0.5, y+1, item.height) self.vg.new('VectorPath', { - name = 'break', stroke = lColours.groupBorder, sequence = seq, dashArray = '1, 1' + name = 'break', stroke = self._groupBorder, sequence = seq, dashArray = '1, 1' }) if self._horizontal then @@ -202,7 +201,7 @@ gui.toolbar = function(Options) end item.border = self.vg.new('VectorRectangle', { - name = 'grouprect', stroke = lColours.groupBorder, fill = 'rgb(0,0,0,20)', strokeWidth = 2, + name = 'grouprect', stroke = self._groupBorder, fill = 'rgb(0,0,0,20)', strokeWidth = 2, x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 }) @@ -224,10 +223,16 @@ gui.toolbar = function(Options) item.vectorIcon.opacity = 0.5 end elseif nz(item.displayName) then + local gradient + if type(self._iconTheme) == 'table' then + gradient = self._iconTheme + else + gradient = gui.iconThemes[self._iconTheme] + end + local gname = 'tbar_grd' .. counter() item.svgGradient = gui.simpleGradient(self.viewport.scene, gname, - { lColours.iconGradient.first, lColours.iconGradient.last }, - 0, item.y, 0, item.height, 'userspace') + gradient, 0, item.y, 0, item.height, 'userspace') item.vectorText = self.vg.new('VectorText', { x = x + self._cellMargin, @@ -309,7 +314,7 @@ gui.toolbar = function(Options) if Icon then pcall(function() - gui.createIcon(self.viewport.scene, 'icons:' .. Icon .. '.svg', self._iconSize, lColours.iconGradient, Icon) + gui.createIcon(self.viewport.scene, Icon, self._iconSize, self._iconTheme, Icon) item.svgGroup = self.viewport.new('VectorRectangle', { fill = 'url(#' .. Icon .. ')', x=0, y=0, width=self._iconSize, height=self._iconSize @@ -358,7 +363,7 @@ gui.toolbar = function(Options) item.selected = false self.viewport.acDraw() else - check(ERR_Search) + raise(ERR_Search) end end @@ -388,7 +393,7 @@ gui.toolbar = function(Options) if redraw then self.viewport.acDraw() end - if (not found) then check(ERR_Search) end + if (not found) then raise(ERR_Search) end end local function tipTimer(Elapsed, CurrentTime) @@ -396,7 +401,7 @@ gui.toolbar = function(Options) gui.tooltip({ text=self._current.tip }) end self._tipTimer = nil - check(ERR_Terminate) + raise(ERR_Terminate) end local function hoverUpdate(Item, StateChanged) -- Highlight items that the user hovers over. @@ -407,9 +412,9 @@ gui.toolbar = function(Options) if self._current and (not self._current.disabled) and (not self._disabled) then if self._clicked then - self._vHover.fill = lColours.selected + self._vHover.fill = self._selectFill else - self._vHover.fill = lColours.hover + self._vHover.fill = self._hoverFill end if self._horizontal then From df865b852cea02dc6bde2a946846387dab285abb Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 21 Nov 2024 23:50:05 +0000 Subject: [PATCH 02/17] [Scripts] Fixed issues with icon naming conflicts --- scripts/gui.fluid | 51 +++++++++++++++++++--------------- scripts/gui/button.fluid | 30 ++++++++++---------- scripts/gui/checkbox.fluid | 4 +-- scripts/gui/combobox.fluid | 4 +-- scripts/gui/menubar.fluid | 41 +++++++-------------------- src/core/microsoft/windows.cpp | 2 +- 6 files changed, 59 insertions(+), 73 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 2939c5418..2d207aab3 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -55,21 +55,6 @@ if (gui == nil) then gui.style = { -- All colour values are fills, so gradient and pattern references are also permitted for 'colours'. - widget = { - text = gui.palette.altText, - bkgd = gui.palette.prime, - stroke = 'rgb(255,255,255,60)', - strokeFocus = 'rgb(255,255,255,160)', - strokeDisabled = 'rgb(255,255,255,60)', - strokeWidth = 2, - width = 160, -- Default width for common widgets (px) - gap = 6, -- Recommended gap between widgets (px) - margin = 6 -- Recommended internal margin for widgets (px) - }, - button = { - bkgd = gui.palette.second, -- Background when raised / default - bkgdActive = gui.palette.contrast -- Background when temporarily clicked (depressed) - }, page = { bkgd = gui.palette.bkgd, -- Background for hosting text & paper-based layouts stroke = gui.palette.prime, -- Border stroke for any area surrounding 'bkgd' @@ -84,10 +69,25 @@ if (gui == nil) then textSelectBkgd = gui.palette.contrast, -- Background fill for 'text' when selected iconSelect = '' -- Recommended icon theme when selected }, + widget = { + text = gui.palette.altText, + bkgd = gui.palette.prime, + stroke = 'rgb(255,255,255,60)', + strokeFocus = 'rgb(255,255,255,160)', + strokeDisabled = 'rgb(255,255,255,60)', + strokeWidth = 2, + width = 160, -- Default width for common widgets (px) + gap = 6, -- Recommended gap between widgets (px) + margin = 6 -- Recommended internal margin for widgets (px) + }, window = { bkgd = gui.palette.neutral, text = 'rgb(0,0,0)' -- Colour for text that is drawn directly to the window }, + button = { + bkgd = gui.palette.invBkgd, -- Default background + bkgdActive = gui.palette.contrast -- Background when temporarily clicked (depressed) + }, menubar = { bkgd = gui.palette.invBkgd, text = gui.palette.altText, @@ -245,7 +245,7 @@ gui.strToRGB = function(Value) end local result - catch(function() + xpcall(function() if (string.byte(Value) == 35) then local colour = tonumber(string.sub(Value,2), 16) if (Value:len() == 7) then @@ -285,8 +285,8 @@ gui.strToRGB = function(Value) result = { r=r, g=g, b=b, a=a } end end, - function(Exception) - error(Exception.message .. '\nInput value: ' .. Value .. '\n' .. debug.traceback()) + function(Error) + error(Error .. '\nInput value: ' .. Value .. '\n' .. debug.traceback()) end) return result @@ -490,14 +490,9 @@ end gui.createIcon = function(Scene, Path, Size, Theme, Name) if not Size then Size = 16 end if not Path then error('No Path specified.') end - if not Name then Name = Path end local lGradient = gui.iconThemes.default local icon = { size=Size } - if Scene.mtFindDef(Name) == ERR_Okay then -- Return if the icon pattern is already registered - return - end - if type(Theme) == 'table' then lGradient = Theme elseif type(Theme) == 'string' then @@ -506,6 +501,14 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) else error('gui.createIcon() theme ' .. Theme .. ' not recognised.') end + + if not Name then Name = Theme:lower() .. '/' .. Path:lower() end + end + + if not Name then Name = Path end + + if Scene.mtFindDef(Name) == ERR_Okay then -- Return if the icon pattern is already registered + return end gui._counter = gui._counter + 1 @@ -551,4 +554,6 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) end check(Scene.mtAddDef(Name, icon.pattern)) + + return { name = Name, theme = Theme, size = Size, path = Path, fill = 'url(#' .. Name .. ')' } end diff --git a/scripts/gui/button.fluid b/scripts/gui/button.fluid index 69020487f..f2caaeaff 100644 --- a/scripts/gui/button.fluid +++ b/scripts/gui/button.fluid @@ -98,24 +98,26 @@ gui.button = function(Options) if Options.icon then pcall(function() - gui.createIcon(self.viewport.scene, Options.icon, ICON_SIZE, iconTheme()) - - self.iconRender = self.viewport.new('VectorViewport', { - x = GAP, - y = (self.viewport.height - ICON_SIZE) * 0.5, - width = ICON_SIZE, - height = ICON_SIZE, - fill = 'url(#' .. Options.icon .. ')' - }) - - label_offset = (GAP * 0.5) + ICON_SIZE + local icon = gui.createIcon(self.viewport.scene, Options.icon, ICON_SIZE, iconTheme()) + + if icon then + self.iconRender = self.viewport.new('VectorViewport', { + x = GAP, + y = (self.viewport.height - icon.size) * 0.5, + width = icon.size, + height = icon.size, + fill = icon.fill + }) + + label_offset = (GAP * 0.5) + ICON_SIZE + end end) end self._textClip = self.viewport.new('VectorViewport', { - x=gui.style.widget.strokeWidth, y=gui.style.widget.strokeWidth, - xOffset=gui.style.widget.strokeWidth, yOffset=gui.style.widget.strokeWidth, - overflow=VOF_HIDDEN + x = gui.style.widget.strokeWidth, y = gui.style.widget.strokeWidth, + xOffset = gui.style.widget.strokeWidth, yOffset = gui.style.widget.strokeWidth, + overflow = VOF_HIDDEN }) self.text = self._textClip.new('VectorText', { diff --git a/scripts/gui/checkbox.fluid b/scripts/gui/checkbox.fluid index 02d1aed38..bb08b0a99 100644 --- a/scripts/gui/checkbox.fluid +++ b/scripts/gui/checkbox.fluid @@ -99,14 +99,14 @@ gui.checkbox = function(Options) strokeWidth = gui.style.page.strokeWidth, }) - gui.createIcon(self.viewport.scene, 'items/checkmark', ICON_SIZE, iconTheme()) + local chk_icon = gui.createIcon(self.viewport.scene, 'items/checkmark', ICON_SIZE, iconTheme()) self.iconRender = self.viewport.new('VectorViewport', { x = (HEIGHT - ICON_SIZE) * 0.5, y = (self.viewport.height - ICON_SIZE) * 0.5, width = ICON_SIZE, height = ICON_SIZE, - fill = 'url(#items/checkmark)', + fill = chk_icon.fill, opacity = (function() if self.state == 1 then return 1.0 diff --git a/scripts/gui/combobox.fluid b/scripts/gui/combobox.fluid index 7b139739a..bcdc6df8d 100644 --- a/scripts/gui/combobox.fluid +++ b/scripts/gui/combobox.fluid @@ -140,14 +140,14 @@ gui.combobox = function(Options) }) local ICON_SIZE = FONT_HEIGHT - gui.createIcon(self.viewport.scene, 'arrows/down', ICON_SIZE, iconTheme()) + local down_icon = gui.createIcon(self.viewport.scene, 'arrows/down', ICON_SIZE, iconTheme()) self.iconRender = self.viewport.new('VectorViewport', { xOffset = MARGIN, y = (self.viewport.height - ICON_SIZE) * 0.5, width = ICON_SIZE, height = ICON_SIZE, - fill = 'url(#arrows/down)' + fill = down_icon.fill }) if not Options.flushEdges then diff --git a/scripts/gui/menubar.fluid b/scripts/gui/menubar.fluid index 3b6d84f28..22f3912ec 100644 --- a/scripts/gui/menubar.fluid +++ b/scripts/gui/menubar.fluid @@ -122,10 +122,10 @@ local function arrangeItems(self) item.gfxWidth = item.width local iconWidth = 0 - if item.svg then + if item.icon then iconWidth = ICON_SIZE + VMARGIN - item.svgGroup.x = x + VMARGIN - item.svgGroup.y = y + VMARGIN + item.icon.x = x + VMARGIN + item.icon.y = y + VMARGIN if self._disabled or item.disabled then item.vectorIcon.opacity = 0.5 end @@ -189,31 +189,10 @@ gui.menubar = function(Options) item.menuConfig = Menu if Icon then - catch(function() - local gname = 'menu_grd' .. counter() - item.svgGroup = self.viewport.new('VectorViewport', { x=0, y=0, width=ICON_SIZE, height=ICON_SIZE }) - item.svgGradient = gui.simpleGradient(self.viewport.scene, gname, { lColours.iconGradient.first, lColours.iconGradient.last }, 0, 0, 0, ICON_SIZE, 'userspace', true) - item.svg = obj.new('svg', { target=item.svgGroup, path='icons:' .. Icon .. '.svg' }) - applyFill(item.svgGroup, 'url(#' .. gname .. ')') - - local viewport = item.svgGroup.child - if (viewport != nil) then - while (child != nil) do - if (child.class.id == ID_VECTORVIEWPORT) then - viewport = child - break - end - end - - if (viewport.viewHeight < viewport.viewWidth) then - item.svgGradient.y1 = viewport.viewY - ((viewport.viewWidth - viewport.viewHeight) * 0.5) - item.svgGradient.y2 = item.svgGradient.y1 + viewport.viewWidth - else - item.svgGradient.y1 = viewport.viewY - item.svgGradient.y2 = viewport.viewY + viewport.viewHeight - end - end - end) + local icon = gui.createIcon(self.viewport.scene, Icon, ICON_SIZE, 'pearl') + item.icon = self.viewport.new('VectorRectangle', { + x=0, y=0, width=ICON_SIZE, height=ICON_SIZE, fill = icon.fill + }) end self._arranged = false @@ -259,7 +238,7 @@ gui.menubar = function(Options) -- Main entry point - catch(function() + xpcall(function() if (not Options.target) then error('A target viewport is required.') end self.viewport = Options.target.new('vectorviewport') @@ -390,8 +369,8 @@ gui.menubar = function(Options) arrangeItems(self) end, - function(Exception) - error('Failed to initialise Menubar: ' .. Exception.message) + function(Error) + print(Error .. '\n' .. debug.traceback()) end) return self diff --git a/src/core/microsoft/windows.cpp b/src/core/microsoft/windows.cpp index 935dc4157..b1be37c38 100644 --- a/src/core/microsoft/windows.cpp +++ b/src/core/microsoft/windows.cpp @@ -1268,7 +1268,7 @@ extern "C" void winEnumSpecialFolders(void (*enumfolder)(const char *, const cha //{ CSIDL_PRINTERS, "printers:", "Printers", "devices/printer" }, //{ CSIDL_DRIVES, "computer:", "My Computer", "programs/filemanager" } { CSIDL_NETHOOD, "network:", "Network Places", "devices/network", 0 }, - { CSIDL_PERSONAL, "documents:", "Documents", "office/documents", 0 }, + { CSIDL_PERSONAL, "documents:", "Documents", "page/multiple", 0 }, { CSIDL_DESKTOPDIRECTORY, "desktop:", "Desktop", "devices/harddisk", 0 } }; char path[MAX_PATH]; From cc11c0e80d004a89f3835ae4b421e9b75b280774 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Fri, 22 Nov 2024 11:45:18 +0000 Subject: [PATCH 03/17] [Scripts] Fixed issues with the divider patterns --- scripts/gui/divider.fluid | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/scripts/gui/divider.fluid b/scripts/gui/divider.fluid index 167052f97..2081d664e 100644 --- a/scripts/gui/divider.fluid +++ b/scripts/gui/divider.fluid @@ -30,14 +30,18 @@ local function arrangeAll(self, Middle) if (self._orientation == 'h') then local margin = 0 - self._div = { x = a_len+1, y = self._primary.y + margin, width = GAP-2, height = self._primary.height - (margin*2) } - self._pattern.x = self._vp.absX + a_len - self._pattern.y = self._primary.absY + margin + self._div = { x = a_len, y = self._primary.y + margin, width = GAP, height = self._primary.height - (margin*2) } + if self._pattern then + self._pattern.x = self._vp.absX + a_len + self._pattern.y = self._primary.absY + margin + end else local margin = 0 - self._div = { x = self._primary.x + margin, y = a_len+1, width = self._primary.width - (margin*2), height = GAP-2 } - self._pattern.x = self._primary.absX + margin - self._pattern.y = self._vp.absY + a_len + self._div = { x = self._primary.x + margin, y = a_len, width = self._primary.width - (margin*2), height = GAP } + if self._pattern then + self._pattern.x = self._primary.absX + margin + self._pattern.y = self._vp.absY + a_len + end end self._secondary[self._anchor] = a_len + GAP @@ -103,22 +107,23 @@ gui.divider = function(Options) local div_name = 'divider-bkgd-' .. self._vp.id if (Options.style != 'none') then + local opacity = 0.15 if (self._orientation == 'h') then -- Top to bottom tiling self._pattern = self._vp.scene.new('VectorPattern', { - name='div_pattern', x=0, y=0, pageWidth=GAP, pageHeight=GAP*4, width=GAP, height=GAP*4, units=VUNIT_USERSPACE, spreadMethod=VSPREAD_REPEAT + name = 'div_pattern', x = 0, y = 0, pageWidth = GAP, pageHeight = GAP*4, width = GAP, height = GAP*4, + units = VUNIT_USERSPACE, spreadMethod = VSPREAD_REPEAT }) - local pt = string.format('0,0, %d,%d, %d,%d 0,%d', GAP, GAP, GAP, GAP*4, GAP) - self._pattern.viewport.new('VectorRectangle', { fill='rgb(0,0,0,20)' }) - self._pattern.viewport.new('VectorPolygon', { points=pt, fill='rgb(0,0,0,20)' }) + + local pt = string.format('%d,%d, %d,%d, %d,%d %d,%d', 0, 0, GAP, GAP*2, GAP, GAP*4, 0, GAP*2) + self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.invBkgd, opacity = opacity }) else -- Left to right tiling self._pattern = self._vp.scene.new('VectorPattern', { - name='div_pattern', x=0, y=0, pageWidth=GAP*3, pageHeight=GAP, width=GAP*3, height=GAP, units=VUNIT_USERSPACE, spreadMethod=VSPREAD_REPEAT + name = 'div_pattern', x = 0, y = 0, pageWidth = GAP*4, pageHeight=GAP, width=GAP*4, height=GAP, + units = VUNIT_USERSPACE, spreadMethod = VSPREAD_REPEAT }) - local pt = string.format('0,0, %d,0, %d,%d %d,%d', GAP, GAP*2, GAP, GAP, GAP) - self._pattern.viewport.new('VectorPolygon', { points=pt, fill='rgb(0,0,0,20)' }) - local pt = string.format('%d,0, %d,0, %d,%d %d,%d', GAP, GAP+GAP, GAP*2+GAP, GAP, GAP+GAP, GAP) - self._pattern.viewport.new('VectorPolygon', { points=pt, fill='rgb(0,0,0,40)' }) + local pt = string.format('%d,%d, %d,%d, %d,%d %d,%d', GAP*2, 0, GAP*4, 0, GAP*2, GAP, 0, GAP) + self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.invBkgd, opacity = opacity }) end check(self._vp.scene.mtAddDef(div_name, self._pattern)) @@ -140,7 +145,7 @@ gui.divider = function(Options) if (Options.style != 'none') then self._dividerVP.new('VectorRectangle', { - name='div_fill', x=0, y=0, width='100%', height='100%', fill='url(#' .. div_name .. ')' + name='div_fill', x='1%', y='1%', width='98%', height='98%', fill='url(#' .. div_name .. ')' }) end From c939d000ac74a77c7caa7ae34b4a3cb0acacb2ed Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Fri, 22 Nov 2024 12:31:13 +0000 Subject: [PATCH 04/17] [Scripts] Visual improvements for FileView and ToolTip --- scripts/gui/fileview.fluid | 8 +++++--- scripts/gui/tooltip.fluid | 19 ++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index 6fabdef3f..2ad6d0f93 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -205,16 +205,18 @@ local function displayFilters(self) if (#self.filterList < 1) then return end if not self.filterViewport then -- Construct the filter list + local bkgd = gui.interpolateRGB(gui.strToRGB(gui.palette.invBkgd), gui.strToRGB(gui.palette.bkgd), 0.1) + self.filterViewport = self.view.viewport.new('VectorViewport', { visibility = VIS_HIDDEN, x = 0, y = self.toolbar.viewport.height, xOffset = 0, height = 1 }) - self.filterViewport.new('VectorRectangle', { width='100%', height='100%', fill='rgb(80,80,90)' }) + self.filterViewport.new('VectorRectangle', { width='100%', height='100%', fill=gui.rgbToSVG(bkgd) }) local height = 2, text_x, text_y, item_height, descent for _, item in ipairs(self.filterList) do item.vp = self.filterViewport.new('VectorViewport', { x = 0, y = height, width = '100%', height = 1 }) - item.highlight = item.vp.new('VectorRectangle', { x = 0, y = 0, width='100%', height='100%', fill='#ff5b00', visibility = VIS_HIDDEN }) - item.txt = item.vp.new('VectorText', { x=0, y=0, string=item.name, fill='rgb(255,255,255)', face='Noto Sans', fontSize=12 }) + item.highlight = item.vp.new('VectorRectangle', { x = 0, y = 0, width='100%', height='100%', fill=gui.palette.contrast, visibility = VIS_HIDDEN }) + item.txt = item.vp.new('VectorText', { x=0, y=0, string=item.name, fill=gui.palette.altText, face='Noto Sans', fontSize=12 }) if not descent then descent = math.round(item.txt.descent) height = descent diff --git a/scripts/gui/tooltip.fluid b/scripts/gui/tooltip.fluid index 5e969df87..f4b35a10d 100644 --- a/scripts/gui/tooltip.fluid +++ b/scripts/gui/tooltip.fluid @@ -6,9 +6,6 @@ Documentation is available in the Parasol Wiki. if (gui == nil) then gui = { } end -local FONT_FACE = 'Noto Sans' -local FONT_SIZE = 11 - gui.tooltip = function(Options) local self = { } -- Public variables @@ -33,7 +30,7 @@ gui.tooltip = function(Options) self.box = self.viewport.new('VectorRectangle', { x = 1, y = 1, height = '100%', width = '100%', - stroke = '#606040', fill = '#ffffe0', + stroke = gui.style.page.stroke, fill = gui.style.page.bkgd, strokeWidth = 2, roundX = 4, roundY = 4, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) @@ -43,15 +40,15 @@ gui.tooltip = function(Options) }) self.text = self.viewport.new('VectorText', { - x = FONT_SIZE, + x = gui.fonts.button.size, string = Options.text, - fill = 'rgb(0,0,0)', - face = FONT_FACE, - fontSize = string.format('%.2fpt', FONT_SIZE), + fill = gui.style.page.text, + face = gui.fonts.button.face, + fontSize = string.format('%.2fpt', gui.fonts.button.size), textFlags = VTXF_RASTER }) - self.surface.height = (FONT_SIZE*1.5) + self.text.fontSize + self.surface.height = (gui.fonts.button.size*1.5) + self.text.fontSize self.surface.y = ptr.y - self.surface.height self.text.y = math.floor((self.viewport.height * 0.5) + (self.text.displaySize * 0.5)) @@ -70,8 +67,8 @@ gui.tooltip = function(Options) -- Reduce the width of the tooltip if the text is short. - if (self.surface.width > self.text.textWidth + (FONT_SIZE*2)) then - self.surface.width = (FONT_SIZE*2) + self.text.textWidth + if (self.surface.width > self.text.textWidth + (gui.fonts.button.size * 2)) then + self.surface.width = (gui.fonts.button.size * 2) + self.text.textWidth end -- Adjust the position of the tool tip so that it fits on the screen. From e946aa2625e18f14a626863872cb0e0cb0ee8bde Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Fri, 22 Nov 2024 12:31:40 +0000 Subject: [PATCH 05/17] [Scripts] Added a Ratio parameter to gui.interpolateRGB() and gui.interpolateHSV() --- scripts/gui.fluid | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 2d207aab3..7e2eb3205 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -302,26 +302,29 @@ end ---------------------------------------------------------------------------------------------------------------------- -- Interpolate two RGB colours -gui.interpolateRGB = function(Base, New) +gui.interpolateRGB = function(Base, New, Ratio) + if not Ratio then Ratio = 0.5 end return { - a = Base.a + ((New.a - Base.a) * 0.5), - r = Base.r + ((New.r - Base.r) * 0.5), - g = Base.g + ((New.g - Base.g) * 0.5), - b = Base.b + ((New.b - Base.b) * 0.5) + a = Base.a + ((New.a - Base.a) * Ratio), + r = Base.r + ((New.r - Base.r) * Ratio), + g = Base.g + ((New.g - Base.g) * Ratio), + b = Base.b + ((New.b - Base.b) * Ratio) } end ---------------------------------------------------------------------------------------------------------------------- -- Interpolate two HSV colours -gui.interpolateHSV = function(Base, New) +gui.interpolateHSV = function(Base, New, Ratio) + if not Ratio then Ratio = 0.5 end return { - a = Base.a + ((New.a - Base.a) * 0.5), - h = Base.h + ((New.h - Base.h) * 0.5), - s = Base.s + ((New.s - Base.s) * 0.5), - v = Base.v + ((New.v - Base.v) * 0.5) + a = Base.a + ((New.a - Base.a) * Ratio), + h = Base.h + ((New.h - Base.h) * Ratio), + s = Base.s + ((New.s - Base.s) * Ratio), + v = Base.v + ((New.v - Base.v) * Ratio) } end + ---------------------------------------------------------------------------------------------------------------------- -- Convert HSV table to RGB From 099f83dba3bb8c66cb7aa9fd3dfbb64ff84e65aa Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Fri, 22 Nov 2024 23:10:25 +0000 Subject: [PATCH 06/17] [Scripts] Minor refactoring of palette code --- scripts/gui.fluid | 41 +++++++++++++++++++++++--------------- scripts/gui/button.fluid | 2 +- scripts/gui/divider.fluid | 4 ++-- scripts/gui/fileview.fluid | 10 +++++----- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 7e2eb3205..571f2257a 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -42,13 +42,22 @@ if (gui == nil) then } gui.palettes = { - -- bkgd and invBkgd are light-dark (light theme) or vice versa, and should be of the same hue in the neighbourhood of prime. + -- bkgd is the standard background colour for text. In a light theme it should be near-white. + -- base is the inverse light value of bkgd and should share the same hue as the prime colour. -- prime (primary) and second (secondary prime) are colour pairs of the same hue. -- contrast is a suitable complement to prime and should be of a strongly contrasting hue -- neutral is a default background colour for unpainted areas, usually a light grey tone - ocean = { type = 'light', bkgd = '#ECF0F1', invBkgd = '#2C3E50', prime = '#3498DB', second = '#2980B9', contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - retroTV = { type = 'light', bkgd = '#f4ebb0', invBkgd = '#6a4654', prime = '#78c2b1', second = '#f45c5d', contrast = '#78c2b1', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - template = { type = '', bkgd = '', invBkgd = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } + ocean = { type = 'light', bkgd = '#ECF0F1', base = '#2C3E50', prime = '#3498DB', second = '#2980B9', + contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + orangeApples = { type = 'light', bkgd = '#ffffff', base = '#19302d', prime = '#74a108', second = '#263248', + contrast = '#e89214', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', + contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + sunrise = { type = 'light', bkgd = '#ECF0F1', base = '#687878', prime = '#e2581b', second = '#bd3232', + contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', + contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + template = { type = '', bkgd = '', base = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } } gui.palette = gui.palettes.ocean @@ -57,17 +66,17 @@ if (gui == nil) then -- All colour values are fills, so gradient and pattern references are also permitted for 'colours'. page = { bkgd = gui.palette.bkgd, -- Background for hosting text & paper-based layouts - stroke = gui.palette.prime, -- Border stroke for any area surrounding 'bkgd' + stroke = gui.palette.base, -- Border stroke for any area surrounding 'bkgd' strokeFocus = gui.palette.contrast, -- Stroke to use when 'border' has the user's focus strokeWidth = 2, text = gui.palette.text, -- Default colour for text drawn over 'bkgd' - icon = '', -- Default icon theme for icons drawn over 'bkgd' - textHighlight = gui.palette.altText, -- Colour for 'text' during user hover - textHighlightBkgd = gui.palette.prime, -- Background fill for 'text' during user hover - iconHighlight = '', -- Recommended icon theme when highlighted + icon = nil, -- Default icon theme for icons drawn over 'bkgd' + textHighlight = gui.palette.altText, -- Colour for 'text' during user hover + textHighlightBkgd = gui.palette.contrast, -- Background fill for 'text' during user hover + iconHighlight = nil, -- Recommended icon theme when highlighted textSelect = gui.palette.altText, -- Colour for 'text' when selected - textSelectBkgd = gui.palette.contrast, -- Background fill for 'text' when selected - iconSelect = '' -- Recommended icon theme when selected + textSelectBkgd = gui.palette.prime, -- Background fill for 'text' when selected + iconSelect = nil -- Recommended icon theme when selected }, widget = { text = gui.palette.altText, @@ -82,14 +91,14 @@ if (gui == nil) then }, window = { bkgd = gui.palette.neutral, - text = 'rgb(0,0,0)' -- Colour for text that is drawn directly to the window + text = 'rgb(0,0,0)' }, button = { - bkgd = gui.palette.invBkgd, -- Default background - bkgdActive = gui.palette.contrast -- Background when temporarily clicked (depressed) + bkgd = gui.palette.base, + highlight = gui.palette.contrast }, menubar = { - bkgd = gui.palette.invBkgd, + bkgd = gui.palette.base, text = gui.palette.altText, highlight = gui.palette.contrast }, @@ -511,7 +520,7 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) if not Name then Name = Path end if Scene.mtFindDef(Name) == ERR_Okay then -- Return if the icon pattern is already registered - return + return { name = Name, theme = Theme, size = Size, path = Path, fill = 'url(#' .. Name .. ')' } end gui._counter = gui._counter + 1 diff --git a/scripts/gui/button.fluid b/scripts/gui/button.fluid index f2caaeaff..9b2554db7 100644 --- a/scripts/gui/button.fluid +++ b/scripts/gui/button.fluid @@ -161,7 +161,7 @@ gui.button = function(Options) self.viewport.mtSubscribeInput(bit.bor(JTYPE_CROSSING, JTYPE_BUTTON, JTYPE_REPEATED), function(Viewport, Msg) repeat if (Msg.type == JET_CROSSED_IN) then - self.bkgd.fill = gui.style.button.bkgdActive + self.bkgd.fill = gui.style.button.highlight if self.events.cursorEntry then self.events.cursorEntry(self) end diff --git a/scripts/gui/divider.fluid b/scripts/gui/divider.fluid index 2081d664e..dcef0c8e4 100644 --- a/scripts/gui/divider.fluid +++ b/scripts/gui/divider.fluid @@ -115,7 +115,7 @@ gui.divider = function(Options) }) local pt = string.format('%d,%d, %d,%d, %d,%d %d,%d', 0, 0, GAP, GAP*2, GAP, GAP*4, 0, GAP*2) - self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.invBkgd, opacity = opacity }) + self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.base, opacity = opacity }) else -- Left to right tiling self._pattern = self._vp.scene.new('VectorPattern', { name = 'div_pattern', x = 0, y = 0, pageWidth = GAP*4, pageHeight=GAP, width=GAP*4, height=GAP, @@ -123,7 +123,7 @@ gui.divider = function(Options) }) local pt = string.format('%d,%d, %d,%d, %d,%d %d,%d', GAP*2, 0, GAP*4, 0, GAP*2, GAP, 0, GAP) - self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.invBkgd, opacity = opacity }) + self._pattern.viewport.new('VectorPolygon', { points = pt, fill = gui.palette.base, opacity = opacity }) end check(self._vp.scene.mtAddDef(div_name, self._pattern)) diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index 2ad6d0f93..c7c062fe0 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -205,7 +205,7 @@ local function displayFilters(self) if (#self.filterList < 1) then return end if not self.filterViewport then -- Construct the filter list - local bkgd = gui.interpolateRGB(gui.strToRGB(gui.palette.invBkgd), gui.strToRGB(gui.palette.bkgd), 0.1) + local bkgd = gui.interpolateRGB(gui.strToRGB(gui.palette.base), gui.strToRGB(gui.palette.bkgd), 0.1) self.filterViewport = self.view.viewport.new('VectorViewport', { visibility = VIS_HIDDEN, x = 0, y = self.toolbar.viewport.height, xOffset = 0, height = 1 @@ -215,7 +215,7 @@ local function displayFilters(self) local height = 2, text_x, text_y, item_height, descent for _, item in ipairs(self.filterList) do item.vp = self.filterViewport.new('VectorViewport', { x = 0, y = height, width = '100%', height = 1 }) - item.highlight = item.vp.new('VectorRectangle', { x = 0, y = 0, width='100%', height='100%', fill=gui.palette.contrast, visibility = VIS_HIDDEN }) + item.highlight = item.vp.new('VectorRectangle', { x = 0, y = 0, width='100%', height='100%', fill=gui.palette.prime, visibility = VIS_HIDDEN }) item.txt = item.vp.new('VectorText', { x=0, y=0, string=item.name, fill=gui.palette.altText, face='Noto Sans', fontSize=12 }) if not descent then descent = math.round(item.txt.descent) @@ -1473,13 +1473,13 @@ gui.fileview = function(View, Options) if Options.navigationBar then local bkgd = self.view.viewport.new('VectorRectangle', { - x=0, y=0, xOffset=0, height=1, fill='#2C3E50' + x=0, y=0, xOffset=0, height=1, fill=gui.palette.base }) self.toolbar = gui.toolbar({ iconTheme = 'pearl', - hoverFill = '#E74C3C', - selectFill = '#2980B9', + hoverFill = gui.style.button.highlight, + selectFill = gui.palette.prime, target = self.view.viewport, x = 0, y = 0, From 66ecbb4eba00d677e5a210493c1b4381e78d4b86 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Sat, 23 Nov 2024 00:40:01 +0000 Subject: [PATCH 07/17] [Scripts] Enhancements to toolbar presentation --- scripts/gui.fluid | 395 +++++++++++++++++++------------------- scripts/gui/toolbar.fluid | 230 ++++++++++++---------- 2 files changed, 330 insertions(+), 295 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 571f2257a..93366f6bc 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -1,7 +1,7 @@ -- Graphical User Interface functions -if (gui == nil) then - -- The following values are the default 'light' theme. They can be overridden by the "style:values.xml" file. +if gui then return end + gui = { theme = 'light', dpi = 160, @@ -11,200 +11,6 @@ if (gui == nil) then end } - gui.fonts = { - -- Use percentages for scalable sizes, where 100% is equivalent to the value of gui.interface.fontSize - default = { face='Noto Sans,Source Sans Pro', size='100%' }, - window = { face='Noto Sans,Source Sans Pro', size='100%' }, -- For text that is not inside a widget - button = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside buttons - icon = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text at the bottom of icons - menu = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Menu item text - titlebar = { face='Noto Sans', size='100%' }, -- Window titlebar text - small = { face='Tiny', size='60%' }, -- Recommended text for small sizes - large = { face='Noto Sans,Source Sans Pro', size='130%' }, -- Recommended text for headers - widget = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside widgets (e.g. combobox) - label = { face='Noto Sans,Source Sans Pro::bold', size='100%' } -- Text for labels and outside widgets - } - - gui.iconThemes = { - default = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, - carbon = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, - yellow = { first={ r=0xec,g=0xef,b=0x9e }, last={ r=0xd6,g=0x87,b=0x10 }, stroke={ r=60, g=30, b=30, a=90 } }, - orange = { first={ r=0xFD,g=0xC8,b=0x30 }, last={ r=0xF3,g=0x73,b=0x35 } }, - blue = { first={ r=0x4b,g=0x6c,b=0xb7 }, last={ r=0x18,g=0x28,b=0x48 } }, - ocean = { first={ r=0xA8,g=0xC0,b=0xFF }, last={ r=0x3F,g=0x2B,b=0x96 } }, - green = { first={ r=0x7e,g=0x9d,b=0x13 }, last={ r=0x00,g=0x65,b=0x32 } }, - red = { first={ r=0xf5,g=0xaf,b=0x19 }, last={ r=0xf1,g=0x27,b=0x11 } }, - indigo = { first={ r=0xad,g=0x53,b=0x89 }, last={ r=0x3c,g=0x10,b=0x53 } }, - rose = { first={ r=0xff,g=0xc3,b=0xa0 }, last={ r=0xFF,g=0xAF,b=0xBD } }, - pink = { first={ r=0xff,g=0xdd,b=0xe1 }, last={ r=0xee,g=0x9c,b=0xa7 } }, - grey = { first={ r=100,g=100,b=100 }, last={ r=70,g=70,b=70 } }, - pearl = { first={ r=250,g=249,b=248 }, last={ r=210,g=211,b=212 } } - } - - gui.palettes = { - -- bkgd is the standard background colour for text. In a light theme it should be near-white. - -- base is the inverse light value of bkgd and should share the same hue as the prime colour. - -- prime (primary) and second (secondary prime) are colour pairs of the same hue. - -- contrast is a suitable complement to prime and should be of a strongly contrasting hue - -- neutral is a default background colour for unpainted areas, usually a light grey tone - ocean = { type = 'light', bkgd = '#ECF0F1', base = '#2C3E50', prime = '#3498DB', second = '#2980B9', - contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - orangeApples = { type = 'light', bkgd = '#ffffff', base = '#19302d', prime = '#74a108', second = '#263248', - contrast = '#e89214', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', - contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - sunrise = { type = 'light', bkgd = '#ECF0F1', base = '#687878', prime = '#e2581b', second = '#bd3232', - contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', - contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - template = { type = '', bkgd = '', base = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } - } - - gui.palette = gui.palettes.ocean - - gui.style = { - -- All colour values are fills, so gradient and pattern references are also permitted for 'colours'. - page = { - bkgd = gui.palette.bkgd, -- Background for hosting text & paper-based layouts - stroke = gui.palette.base, -- Border stroke for any area surrounding 'bkgd' - strokeFocus = gui.palette.contrast, -- Stroke to use when 'border' has the user's focus - strokeWidth = 2, - text = gui.palette.text, -- Default colour for text drawn over 'bkgd' - icon = nil, -- Default icon theme for icons drawn over 'bkgd' - textHighlight = gui.palette.altText, -- Colour for 'text' during user hover - textHighlightBkgd = gui.palette.contrast, -- Background fill for 'text' during user hover - iconHighlight = nil, -- Recommended icon theme when highlighted - textSelect = gui.palette.altText, -- Colour for 'text' when selected - textSelectBkgd = gui.palette.prime, -- Background fill for 'text' when selected - iconSelect = nil -- Recommended icon theme when selected - }, - widget = { - text = gui.palette.altText, - bkgd = gui.palette.prime, - stroke = 'rgb(255,255,255,60)', - strokeFocus = 'rgb(255,255,255,160)', - strokeDisabled = 'rgb(255,255,255,60)', - strokeWidth = 2, - width = 160, -- Default width for common widgets (px) - gap = 6, -- Recommended gap between widgets (px) - margin = 6 -- Recommended internal margin for widgets (px) - }, - window = { - bkgd = gui.palette.neutral, - text = 'rgb(0,0,0)' - }, - button = { - bkgd = gui.palette.base, - highlight = gui.palette.contrast - }, - menubar = { - bkgd = gui.palette.base, - text = gui.palette.altText, - highlight = gui.palette.contrast - }, - menu = { - bkgd = gui.palette.bkgd, - border = 'rgb(200,200,200)' -- Stroke for menu border - }, - desktop = { - bkgd = 'rgb(160,160,160)' - } - } - - if gui.palette.type == 'light' then - gui.style.page.icon = 'carbon' - gui.style.page.iconHighlight = 'pearl' - gui.style.page.iconSelect = 'pearl' - gui.style.page.text = gui.palette.text - gui.style.page.textHighlight = gui.palette.altText - gui.style.page.textSelect = gui.palette.altText - else - gui.style.page.icon = 'pearl' - gui.style.page.iconHighlight = 'carbon' - gui.style.page.iconSelect = 'carbon' - end - - gui.interface = { - fontSize = 11, -- Default font-size for everything (pt) - iconTheme = 'Default', -- Named folder to load the icons from - iconSize = 20 -- Recommended default size for icons (px) and should be linked to fontSize - } - - function convertFontSizes() -- Convert font percentage sizes to their px size - for k,v in pairs(gui.fonts) do - if gui.fonts[k].size then - if string.match(gui.fonts[k].size, '^%-?%d+%%$') then - pct = tonumber(string.match(gui.fonts[k].size, '^(%-?%d+)%%$')) - gui.fonts[k].size = math.floor(gui.interface.fontSize * (pct / 100)) - end - end - end - end - - local path = 'style:values.xml' - if mSys.AnalysePath(path) != ERR_Okay then - path = 'environment:config/values.xml' - if mSys.AnalysePath(path) != ERR_Okay then - path = 'styles:default/values.xml' - end - end - - local xml = obj.new('xml', { path=path }) - - if (xml != nil) then ---[[ - local err, index = xml.mtFindTag('/colours') - if (err == ERR_Okay) then - for k,v in pairs(gui.style) do - err, colour = xml.mtGetAttrib(index, k) - if (err == ERR_Okay) then gui.style[k] = colour end - end - end ---]] - local err, index = xml.mtFindTag('/interface') - if (err == ERR_Okay) then - for k,v in pairs(gui.interface) do - err, val = xml.mtGetAttrib(index, k) - if (err == ERR_Okay) then - if string.match(val, '^%-?%d+$') then - gui.interface[k] = tonumber(val) - else - gui.interface[k] = val - end - end - end - end - - convertFontSizes() - - local err, index = xml.mtFindTag('/fonts') - if (err == ERR_Okay) then - for k,v in pairs(gui.fonts) do - local err, index = xml.mtFindTag("/fonts/font(@name='" .. k .. "')") - if (err == ERR_Okay) then - local err, face = xml.mtGetAttrib(index, 'face') - if (err == ERR_Okay) then gui.fonts[k].face = face end - local err, size = xml.mtGetAttrib(index, 'size') - if (err == ERR_Okay) then - if string.match(size, '^%-?%d+$') then - gui.fonts[k].size = tonumber(size) - elseif string.match(size, '^%-?%d+%%$') then - pct = tonumber(string.match(size, '^(%-?%d+)%%$')) - gui.fonts[k].size = math.floor(gui.interface.fontSize * (pct / 100)) - else - gui.fonts[k].size = size - end - end - end - end - end - - xml = nil - else - convertFontSizes() - end -end - ---------------------------------------------------------------------------------------------------------------------- gui.getFontHeight = function(Font) @@ -569,3 +375,200 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) return { name = Name, theme = Theme, size = Size, path = Path, fill = 'url(#' .. Name .. ')' } end + +---------------------------------------------------------------------------------------------------------------------- + + -- The following values are the default 'light' theme. They can be overridden by the "style:values.xml" file. + + gui.fonts = { + -- Use percentages for scalable sizes, where 100% is equivalent to the value of gui.interface.fontSize + default = { face='Noto Sans,Source Sans Pro', size='100%' }, + window = { face='Noto Sans,Source Sans Pro', size='100%' }, -- For text that is not inside a widget + button = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside buttons + icon = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Labeling for icons + menu = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Menu item text + titlebar = { face='Noto Sans', size='100%' }, -- Window titlebar text + small = { face='Tiny', size='60%' }, -- Recommended text for small sizes + large = { face='Noto Sans,Source Sans Pro', size='130%' }, -- Recommended text for headers + widget = { face='Noto Sans,Source Sans Pro', size='100%' }, -- Text inside widgets (e.g. combobox) + label = { face='Noto Sans,Source Sans Pro::bold', size='100%' } -- Text for labels and outside widgets + } + + gui.iconThemes = { + default = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, + carbon = { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }, + yellow = { first={ r=0xec,g=0xef,b=0x9e }, last={ r=0xd6,g=0x87,b=0x10 }, stroke={ r=60, g=30, b=30, a=90 } }, + orange = { first={ r=0xFD,g=0xC8,b=0x30 }, last={ r=0xF3,g=0x73,b=0x35 } }, + blue = { first={ r=0x4b,g=0x6c,b=0xb7 }, last={ r=0x18,g=0x28,b=0x48 } }, + ocean = { first={ r=0xA8,g=0xC0,b=0xFF }, last={ r=0x3F,g=0x2B,b=0x96 } }, + green = { first={ r=0x7e,g=0x9d,b=0x13 }, last={ r=0x00,g=0x65,b=0x32 } }, + red = { first={ r=0xf5,g=0xaf,b=0x19 }, last={ r=0xf1,g=0x27,b=0x11 } }, + indigo = { first={ r=0xad,g=0x53,b=0x89 }, last={ r=0x3c,g=0x10,b=0x53 } }, + rose = { first={ r=0xff,g=0xc3,b=0xa0 }, last={ r=0xFF,g=0xAF,b=0xBD } }, + pink = { first={ r=0xff,g=0xdd,b=0xe1 }, last={ r=0xee,g=0x9c,b=0xa7 } }, + grey = { first={ r=100,g=100,b=100 }, last={ r=70,g=70,b=70 } }, + pearl = { first={ r=250,g=249,b=248 }, last={ r=210,g=211,b=212 } } + } + + gui.palettes = { + -- bkgd is the standard background colour for text. In a light theme it should be near-white. + -- base is the inverse light value of bkgd and should share the same hue as the prime colour. + -- prime (primary) and second (secondary prime) are colour pairs of the same hue. + -- contrast is a suitable complement to prime and should be of a strongly contrasting hue + -- neutral is a default background colour for unpainted areas, usually a light grey tone + ocean = { type = 'light', bkgd = '#ECF0F1', base = '#2C3E50', prime = '#3498DB', second = '#2980B9', + contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + orangeApples = { type = 'light', bkgd = '#ffffff', base = '#19302d', prime = '#74a108', second = '#263248', + contrast = '#e89214', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', + contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + sunrise = { type = 'light', bkgd = '#ECF0F1', base = '#687878', prime = '#e2581b', second = '#bd3232', + contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', + contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + template = { type = '', bkgd = '', base = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } + } + + gui.palette = gui.palettes.ocean + + gui.style = { + -- All colour values are fills, so gradient and pattern references are also permitted for 'colours'. + page = { + bkgd = gui.palette.bkgd, -- Background for hosting text & paper-based layouts + stroke = gui.palette.base, -- Border stroke for any area surrounding 'bkgd' + strokeFocus = gui.palette.contrast, -- Stroke to use when 'border' has the user's focus + strokeWidth = 2, + text = gui.palette.text, -- Default colour for text drawn over 'bkgd' + icon = nil, -- Default icon theme for icons drawn over 'bkgd' + textHighlight = gui.palette.altText, -- Colour for 'text' during user hover + textHighlightBkgd = gui.palette.contrast, -- Background fill for 'text' during user hover + iconHighlight = nil, -- Recommended icon theme when highlighted + textSelect = gui.palette.altText, -- Colour for 'text' when selected + textSelectBkgd = gui.palette.prime, -- Background fill for 'text' when selected + iconSelect = nil -- Recommended icon theme when selected + }, + widget = { + text = gui.palette.altText, + bkgd = gui.palette.prime, + stroke = 'rgb(255,255,255,60)', + strokeFocus = gui.palette.contrast, //'rgb(255,255,255,160)', + strokeDisabled = 'rgb(255,255,255,60)', + strokeWidth = 2, + width = 160, -- Default width for common widgets (px) + gap = 6, -- Recommended gap between widgets (px) + margin = 6 -- Recommended internal margin for widgets (px) + }, + window = { + bkgd = gui.palette.neutral, + text = 'rgb(0,0,0)' + }, + button = { + bkgd = gui.palette.base, + highlight = gui.palette.contrast + }, + menubar = { + bkgd = gui.palette.base, + text = gui.palette.altText, + highlight = gui.palette.contrast + }, + menu = { + bkgd = gui.palette.bkgd, + border = 'rgb(200,200,200)' -- Stroke for menu border + }, + desktop = { + bkgd = 'rgb(160,160,160)' + } + } + + if gui.palette.type == 'light' then + gui.style.page.icon = 'carbon' + gui.style.page.iconHighlight = 'pearl' + gui.style.page.iconSelect = 'pearl' + gui.style.page.text = gui.palette.text + gui.style.page.textHighlight = gui.palette.altText + gui.style.page.textSelect = gui.palette.altText + else + gui.style.page.icon = 'pearl' + gui.style.page.iconHighlight = 'carbon' + gui.style.page.iconSelect = 'carbon' + end + + gui.interface = { + fontSize = 11, -- Default font-size for everything (pt) + iconTheme = 'Default', -- Named folder to load the icons from + iconSize = 20 -- Recommended default size for icons (px) and should be linked to fontSize + } + + function convertFontSizes() -- Convert font percentage sizes to their px size + for k,v in pairs(gui.fonts) do + if gui.fonts[k].size then + if string.match(gui.fonts[k].size, '^%-?%d+%%$') then + pct = tonumber(string.match(gui.fonts[k].size, '^(%-?%d+)%%$')) + gui.fonts[k].size = math.floor(gui.interface.fontSize * (pct / 100)) + end + end + end + end + + local path = 'style:values.xml' + if mSys.AnalysePath(path) != ERR_Okay then + path = 'environment:config/values.xml' + if mSys.AnalysePath(path) != ERR_Okay then + path = 'styles:default/values.xml' + end + end + + local xml = obj.new('xml', { path=path }) + + if (xml != nil) then +--[[ + local err, index = xml.mtFindTag('/colours') + if (err == ERR_Okay) then + for k,v in pairs(gui.style) do + err, colour = xml.mtGetAttrib(index, k) + if (err == ERR_Okay) then gui.style[k] = colour end + end + end +--]] + local err, index = xml.mtFindTag('/interface') + if (err == ERR_Okay) then + for k,v in pairs(gui.interface) do + err, val = xml.mtGetAttrib(index, k) + if (err == ERR_Okay) then + if string.match(val, '^%-?%d+$') then + gui.interface[k] = tonumber(val) + else + gui.interface[k] = val + end + end + end + end + + convertFontSizes() + + local err, index = xml.mtFindTag('/fonts') + if (err == ERR_Okay) then + for k,v in pairs(gui.fonts) do + local err, index = xml.mtFindTag("/fonts/font(@name='" .. k .. "')") + if (err == ERR_Okay) then + local err, face = xml.mtGetAttrib(index, 'face') + if (err == ERR_Okay) then gui.fonts[k].face = face end + local err, size = xml.mtGetAttrib(index, 'size') + if (err == ERR_Okay) then + if string.match(size, '^%-?%d+$') then + gui.fonts[k].size = tonumber(size) + elseif string.match(size, '^%-?%d+%%$') then + pct = tonumber(string.match(size, '^(%-?%d+)%%$')) + gui.fonts[k].size = math.floor(gui.interface.fontSize * (pct / 100)) + else + gui.fonts[k].size = size + end + end + end + end + end + + xml = nil + else + convertFontSizes() + end diff --git a/scripts/gui/toolbar.fluid b/scripts/gui/toolbar.fluid index 0dcbc1570..8b4244e2e 100644 --- a/scripts/gui/toolbar.fluid +++ b/scripts/gui/toolbar.fluid @@ -22,20 +22,21 @@ end gui.toolbar = function(Options) local self = { -- Public variables - host = nil, - viewport = nil, -- The main viewport for the toolbar - _groupMargin = 4, - _groupCount = 0, - _round = 4, - _cellMargin = nz(Options.cellMargin, 6), - _disabled = false, - _lookup = { }, - _iconSize = nz(Options.iconSize, gui.interface.iconSize), - _hasFocus = false, - _iconTheme = nz(Options.iconTheme, { first={ r=90,g=90,b=90 }, last={ r=70,g=70,b=110 } }), - _hoverFill = nz(Options.hoverFill, 'rgb(0,0,0,32)'), - _selectFill = nz(Options.selectFill, 'rgb(255,255,255,128)'), - _groupBorder = nz(Options.groupBorder, 'rgb(255,255,255,128)') + host = nil, + viewport = nil, -- The main viewport for the toolbar + _groupMargin = 4, + _groupCount = 0, + _round = 4, + _cellMargin = nz(Options.cellMargin, 6), + _disabled = false, + _lookup = { }, + _iconSize = nz(Options.iconSize, gui.interface.iconSize), + _hasFocus = false, + _iconTheme = nz(Options.iconTheme, gui.style.page.icon), + _iconHighlight = nz(Options.iconHighlight, gui.style.page.iconHighlight), + _hoverFill = nz(Options.hoverFill, gui.style.button.highlight), + _selectFill = nz(Options.selectFill, gui.palette.prime), + _groupBorder = nz(Options.groupBorder, gui.style.widget.stroke) } local function sendFeedback() @@ -87,7 +88,7 @@ gui.toolbar = function(Options) local item = self._first while (item) do if (X >= item.x) and (X < item.x + item.width) and (Y >= item.y) and (Y < item.y + item.height) then - if (item.type == TI_ICON) then return item end + if item.type == TI_ICON then return item end end item = item.next end @@ -96,16 +97,16 @@ gui.toolbar = function(Options) -- Highlight selected item (for keyboard item selection) local function highlightItem(Item) - if self.hIcon.x == Item.x then return end + if self.kbIcon.x == Item.x then return end self._current = Item - self.hIcon.x = Item.x - self.hIcon.y = 0 - self.hIcon.width = Item.width - self.hIcon.height = self.viewport.height - self.hIcon.visibility = 'visible' + self.kbIcon.x = Item.x + self.kbIcon.y = 0 + self.kbIcon.width = Item.width + self.kbIcon.height = self.viewport.height + self.kbIcon.visibility = 'visible' if self._groupClip then - self.hIcon.mask = self._groupClip + self.kbIcon.mask = self._groupClip end self.viewport.acDraw() @@ -115,35 +116,33 @@ gui.toolbar = function(Options) local function arrangeItems() local groupOffset = 0 - if (self._groupCount > 0) then + if self._groupCount > 0 then groupOffset = self._groupMargin end - if (not self._vHover) then -- Hover highlight - if (self._groupCount > 0) then - self._vHover = self.viewport.new('VectorRectangle', { - fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden' - }) - self._vHover.acMoveToBack() - else - self._vHover = self.viewport.new('VectorRectangle', { - fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden', roundX=self._round, roundY=self._round - }) - self._vHover.acMoveToBack() - end - end - - -- The _groupClip simplifies masking and 'vg' provides a container for hosting temporary vectors. + -- The _groupClip simplifies masking and 'vg' provides a container for hosting vectors. if self._groupClip then self._groupClip.free() end if self.vg then self.vg.free() end - if (self._groupCount > 0) then + if self._groupCount > 0 then self._groupClip = self.viewport.scene.new('VectorClip', { name='GroupMask', units='UserSpace' }) end self.vg = self.viewport.new('VectorGroup', { name='ItemGroup' }) + if not self._vHover then -- Hover highlight + if self._groupCount > 0 then + self._vHover = self.vg.new('VectorRectangle', { + fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden' + }) + else + self._vHover = self.vg.new('VectorRectangle', { + fill=self._hoverFill, x=0, y=0, width=1, height=1, visibility='hidden', roundX=self._round, roundY=self._round + }) + end + end + local item = self._first local x, y @@ -158,7 +157,7 @@ gui.toolbar = function(Options) local currentGroup = nil local spanCount = 0 while item do - if (item.type == TI_BREAK) then + if item.type == TI_BREAK then item.x = x item.y = y item.width = 6 @@ -174,7 +173,7 @@ gui.toolbar = function(Options) else y = y + item.height end - elseif (item.type == TI_GROUP) then + elseif item.type == TI_GROUP then if self._horizontal then if not item.prev then x = x + groupOffset @@ -200,8 +199,14 @@ gui.toolbar = function(Options) item.width = self._iconSize + (self._cellMargin * 2) end + item.bkgd = self.vg.new('VectorRectangle', { fill = gui.style.page.bkgd, + name = 'groupbkgd', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 + }) + + item.bkgd.acMoveToBack() + item.border = self.vg.new('VectorRectangle', { - name = 'grouprect', stroke = self._groupBorder, fill = 'rgb(0,0,0,20)', strokeWidth = 2, + name = 'grouprect', stroke = gui.style.button.bkgd, strokeWidth = gui.style.widget.strokeWidth, x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 }) @@ -210,36 +215,36 @@ gui.toolbar = function(Options) name = 'groupclip', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 }) end - elseif (item.type == TI_ICON) then + elseif item.type == TI_ICON then item.x = x item.y = y item.width = self._iconSize + (self._cellMargin * 2) item.height = self._iconSize + (self._cellMargin * 2) - if item.svgGroup then - item.svgGroup.x = x + self._cellMargin - item.svgGroup.y = y + self._cellMargin + if item.icon then + item.icon.x = x + self._cellMargin + item.icon.y = y + self._cellMargin if self._disabled or item.disabled then item.vectorIcon.opacity = 0.5 end + item.icon.acMoveToFront() elseif nz(item.displayName) then - local gradient - if type(self._iconTheme) == 'table' then - gradient = self._iconTheme - else - gradient = gui.iconThemes[self._iconTheme] + local fill_name = 'tbar_text' .. counter() + gui.simpleGradient(self.viewport.scene, fill_name, gui.iconThemes[self._iconTheme], 0, 0, 0, 1) + item.textFill = 'url(#' .. fill_name .. ')' + + if self._iconHighlight and (self._iconTheme != self._iconHighlight) then + local alt_name = 'tbar_alt' .. counter() + gui.simpleGradient(self.viewport.scene, alt_name, gui.iconThemes[self._iconHighlight], 0, 0, 0, 1) + item.textAltFill = 'url(#' .. alt_name .. ')' end - local gname = 'tbar_grd' .. counter() - item.svgGradient = gui.simpleGradient(self.viewport.scene, gname, - gradient, 0, item.y, 0, item.height, 'userspace') - item.vectorText = self.vg.new('VectorText', { x = x + self._cellMargin, - face = 'Noto Sans', + face = gui.fonts.widget.face, fontSize = self._iconSize * 0.75, weight = 500, - fill = 'url(#' .. gname .. ')', + fill = item.textFill, string = item.displayName }) @@ -269,11 +274,23 @@ gui.toolbar = function(Options) currentGroup.width = self.viewport.width - (groupOffset * 2) currentGroup.height = y - currentGroup.y end - currentGroup.border.width = currentGroup.width - currentGroup.border.height = currentGroup.height - currentGroup.clip.width = currentGroup.width - currentGroup.clip.height = currentGroup.height - if (spanCount == currentGroup.span) then + + if currentGroup.bkgd then + currentGroup.bkgd.width = currentGroup.width + currentGroup.bkgd.height = currentGroup.height + end + + if currentGroup.border then + currentGroup.border.width = currentGroup.width + currentGroup.border.height = currentGroup.height + end + + if currentGroup.clip then + currentGroup.clip.width = currentGroup.width + currentGroup.clip.height = currentGroup.height + end + + if spanCount == currentGroup.span then if self._horizontal then x = x + groupOffset -- Gap between groups else @@ -314,10 +331,16 @@ gui.toolbar = function(Options) if Icon then pcall(function() - gui.createIcon(self.viewport.scene, Icon, self._iconSize, self._iconTheme, Icon) - item.svgGroup = self.viewport.new('VectorRectangle', { - fill = 'url(#' .. Icon .. ')', - x=0, y=0, width=self._iconSize, height=self._iconSize + local info = gui.createIcon(self.viewport.scene, Icon, self._iconSize, self._iconTheme) + item.iconFill = info.fill + + if self._iconHighlight and (self._iconTheme != self._iconHighlight) then + local info = gui.createIcon(self.viewport.scene, Icon, self._iconSize, self._iconHighlight) + item.iconAltFill = info.fill + end + + item.icon = self.viewport.new('VectorRectangle', { + fill = info.fill, x=0, y=0, width=self._iconSize, height=self._iconSize }) end) end @@ -375,10 +398,10 @@ gui.toolbar = function(Options) local redraw = false local found = false local item = firstIcon() - while (item) do - if (item.type == TI_ICON) then - if (item.id == ID) then - if (not item.selected) then + while item do + if item.type == TI_ICON then + if item.id == ID then + if not item.selected then item.selected = true redraw = true found = true @@ -393,7 +416,7 @@ gui.toolbar = function(Options) if redraw then self.viewport.acDraw() end - if (not found) then raise(ERR_Search) end + if not found then raise(ERR_Search) end end local function tipTimer(Elapsed, CurrentTime) @@ -407,7 +430,15 @@ gui.toolbar = function(Options) local function hoverUpdate(Item, StateChanged) -- Highlight items that the user hovers over. if (self._current == Item) and (StateChanged != true) then return end - self.hIcon.visibility = 'hidden' + if self._current then + if self._current.icon then + self._current.icon.fill = self._current.iconFill + elseif self._current.vectorText then + self._current.vectorText.fill = self._current.textFill + end + end + + self.kbIcon.visibility = 'hidden' self._current = Item if self._current and (not self._current.disabled) and (not self._disabled) then @@ -416,6 +447,11 @@ gui.toolbar = function(Options) else self._vHover.fill = self._hoverFill end + if Item.icon and Item.iconAltFill then + Item.icon.fill = Item.iconAltFill + elseif Item.vectorText and Item.textAltFill then + Item.vectorText.fill = Item.textAltFill + end if self._horizontal then self._vHover.x = self._current.x @@ -428,6 +464,7 @@ gui.toolbar = function(Options) self._vHover.width = self.viewport.width self._vHover.height = self._current.height end + self._vHover.visibility = 'visible' if self._groupClip then self._vHover.mask = self._groupClip @@ -442,14 +479,14 @@ gui.toolbar = function(Options) local function inputFeedback(Viewport, Events) -- Internal input handler local ev = Events while ev do - if (ev.type == JET_CROSSED_IN) then + if ev.type == JET_CROSSED_IN then - elseif (ev.type == JET_CROSSED_OUT) then + elseif ev.type == JET_CROSSED_OUT then resetTipTimer() if self._current then hoverUpdate(nil) end - elseif (bit.band(ev.flags, JTYPE_MOVEMENT) != 0) then + elseif bit.band(ev.flags, JTYPE_MOVEMENT) != 0 then resetTipTimer() if self._clicked then return end @@ -466,7 +503,7 @@ gui.toolbar = function(Options) hoverUpdate(item) elseif bit.band(ev.flags, JTYPE_BUTTON) != 0 then - if (ev.value > 0) then + if ev.value > 0 then if self._disabled then return end resetTipTimer() @@ -481,7 +518,7 @@ gui.toolbar = function(Options) hoverUpdate(self._current, true) end end - elseif (ev.type == JET_BUTTON_1) then + elseif ev.type == JET_BUTTON_1 then if self._clicked then self._clicked = nil @@ -489,8 +526,8 @@ gui.toolbar = function(Options) local delta_x = ev.x - lClickX local delta_y = ev.y - lClickY - if (delta_x < 0) then delta_x = -delta_x end - if (delta_y < 0) then delta_y = -delta_y end + if delta_x < 0 then delta_x = -delta_x end + if delta_y < 0 then delta_y = -delta_y end if (delta_x < 4) and (delta_y < 4) then sendFeedback() @@ -505,7 +542,7 @@ gui.toolbar = function(Options) -- Main entry point - if (not Options.target) then error('A target viewport is required.') end + if not Options.target then error('A target viewport is required.') end if Options.groupMargin then self._groupMargin = Options.groupMargin @@ -541,7 +578,7 @@ gui.toolbar = function(Options) self._groupMargin = 0 end - if (self.viewport.init() != ERR_Okay) then error('Failed to initialise viewport.') end + if self.viewport.init() != ERR_Okay then error('Failed to initialise viewport.') end self.viewport.subscribe('free', function(Viewport, Args) if self._tipTimer then mSys.UpdateTimer(self._tipTimer, 0) end @@ -549,10 +586,10 @@ gui.toolbar = function(Options) end) self.viewport.mtSubscribeFeedback(bit.bor(FM_HAS_FOCUS, FM_CHILD_HAS_FOCUS, FM_LOST_FOCUS), function(Viewport, Event) - if (Event == FM_LOST_FOCUS) then + if Event == FM_LOST_FOCUS then self._hasFocus = false if self._current then - self.hIcon.visibility = 'hidden' + self.kbIcon.visibility = 'hidden' self._current = nil end else @@ -572,7 +609,7 @@ gui.toolbar = function(Options) while scan and (scan.type != TI_ICON) do scan = scan.prev end - if (not scan) then scan = lastIcon() end + if not scan then scan = lastIcon() end highlightItem(scan) else highlightItem(firstIcon()) @@ -583,7 +620,7 @@ gui.toolbar = function(Options) while scan and (scan.type != TI_ICON) do scan = scan.next end - if (not scan) then scan = firstIcon() end + if not scan then scan = firstIcon() end highlightItem(scan) else highlightItem(firstIcon()) @@ -594,34 +631,29 @@ gui.toolbar = function(Options) self.viewport.mtSubscribeInput(bit.bor(JTYPE_BUTTON, JTYPE_MOVEMENT, JTYPE_CROSSING), inputFeedback) - local fill = 'rgb(0,0,0,128)' - if (Options.theme != 'light') then - fill = 'rgb(255,255,255,200)' + local kbfill = 'rgb(0,0,0,128)' + if Options.theme != 'light' then + kbfill = 'rgb(255,255,255,200)' end - self.hIcon = self.viewport.new('VectorRectangle', { -- Icon highlighter, for keyboard-based selection - fill = fill, - x = 0, - y = 0, - width = 16, - height = 16, - visibility = 'hidden' + self.kbIcon = self.viewport.new('VectorRectangle', { -- Icon highlighter, for keyboard-based selection + fill = kbfill, x = 0, y = 0, width = 16, height = 16, visibility = 'hidden' }) Options.createItems(self) self._colBreadth = self._iconSize + (self._cellMargin * 2) - if (self._groupCount > 0) then + if self._groupCount > 0 then self._colBreadth = self._colBreadth + (self._groupMargin*2) end - if (Options.orientation == 'horizontal') then + if Options.orientation == 'horizontal' then self._horizontal = true - elseif (Options.orientation == 'vertical') then + elseif Options.orientation == 'vertical' then self._horizontal = false end - if (not self._horizontal) and (Options.y and Options.yOffset) then + if not self._horizontal and (Options.y and Options.yOffset) then self._horizontal = false else self._horizontal = true From 7d2b495e33b020d342d887768cbd3b12cb68207e Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Sun, 24 Nov 2024 16:04:18 +0000 Subject: [PATCH 08/17] [Scripts] Added dropdown script, simplified menu script --- scripts/gui.fluid | 19 ++- scripts/gui/combobox.fluid | 5 +- scripts/gui/dropdown.fluid | 333 +++++++++++++++++++++++++++++++++++++ scripts/gui/menu.fluid | 58 +++---- scripts/gui/menubar.fluid | 1 - scripts/gui/toolbar.fluid | 9 +- 6 files changed, 372 insertions(+), 53 deletions(-) create mode 100644 scripts/gui/dropdown.fluid diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 93366f6bc..bab3c61c9 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -285,8 +285,8 @@ gui.simpleGradient = function(Scene, Name, Colours, X1, Y1, X2, Y2, Units, Ignor Scene.mtAddDef(Name, gradient) end) - if (ex) then - if (ex.code == ERR_ResourceExists) then + if ex then + if ex.code == ERR_ResourceExists then gradient.free() return nil else @@ -311,9 +311,13 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) local lGradient = gui.iconThemes.default local icon = { size=Size } + gui._counter = gui._counter + 1 + local gname = 'icon_grd' .. gui._counter + if type(Theme) == 'table' then lGradient = Theme elseif type(Theme) == 'string' then + gname = 'icon_' .. Theme if gui.iconThemes[Theme] then lGradient = gui.iconThemes[Theme] else @@ -321,6 +325,8 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) end if not Name then Name = Theme:lower() .. '/' .. Path:lower() end + else + error('Invalid Theme specified.') end if not Name then Name = Path end @@ -329,10 +335,8 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) return { name = Name, theme = Theme, size = Size, path = Path, fill = 'url(#' .. Name .. ')' } end - gui._counter = gui._counter + 1 - local gname = 'icon_grd' .. gui._counter icon.pattern = Scene.new('VectorPattern', { pageWidth=Size, pageHeight=Size, spreadMethod=VSPREAD_PAD }) - icon.gradient = gui.simpleGradient(icon.pattern.scene, gname, { lGradient.first, lGradient.last }, 0, 0, 0, 1) + icon.gradient = gui.simpleGradient(icon.pattern.scene, gname, { lGradient.first, lGradient.last }, 0, 0, 0, 1, 'boundingbox', true) if string.find(Path, 'icons:') != 1 then Path = 'icons:' .. Path .. '.svg' end @@ -451,8 +455,7 @@ end text = gui.palette.altText, bkgd = gui.palette.prime, stroke = 'rgb(255,255,255,60)', - strokeFocus = gui.palette.contrast, //'rgb(255,255,255,160)', - strokeDisabled = 'rgb(255,255,255,60)', + strokeFocus = gui.palette.contrast, strokeWidth = 2, width = 160, -- Default width for common widgets (px) gap = 6, -- Recommended gap between widgets (px) @@ -473,7 +476,7 @@ end }, menu = { bkgd = gui.palette.bkgd, - border = 'rgb(200,200,200)' -- Stroke for menu border + stroke = gui.palette.base }, desktop = { bkgd = 'rgb(160,160,160)' diff --git a/scripts/gui/combobox.fluid b/scripts/gui/combobox.fluid index bcdc6df8d..ac9ff4f3d 100644 --- a/scripts/gui/combobox.fluid +++ b/scripts/gui/combobox.fluid @@ -6,7 +6,7 @@ Documentation is available in the Parasol Wiki. require 'common' require 'gui' - require 'gui/menu' + require 'gui/dropdown' local FONT_MAX_HEIGHT, FONT_HEIGHT = gui.getFontHeight(gui.fonts.widget) local BOX_HEIGHT = math.floor(FONT_MAX_HEIGHT * 1.35) + 1 @@ -231,10 +231,9 @@ gui.combobox = function(Options) until Msg == nil end) - self.menu = gui.menu({ + self.menu = gui.dropdown({ relative = self.viewport, client = self.inputViewport, - style = 'list', config = Options.items, lineLimit = 8, feedback = function(Menu, Item) diff --git a/scripts/gui/dropdown.fluid b/scripts/gui/dropdown.fluid new file mode 100644 index 000000000..da156fd31 --- /dev/null +++ b/scripts/gui/dropdown.fluid @@ -0,0 +1,333 @@ +--[[ +Internal script for combobox drop-down menus. +--]] + + require 'common' + require 'gui' + include 'display' + + if (not mGfx) then mGfx = mod.load('display') end + +local ITEM_SPACING = 1.2 + +gui.dropdown = function(Options) + local self = { -- Public variables + surface = nil, + timeShow = 0, + timeHide = 0, + visible = false, + client = Options.client, + feedback = Options.feedback, + x = nz(Options.x, 0), + y = nz(Options.y, 0), + relative = Options.relative, + items = { } + } + + local function initVectorScene() + local GAP_X = 6 + local showIcons = nz(Options.showIcons, true) + local textX = GAP_X + + if showIcons then + local iconsPresent = false + for _, item in ipairs(Options.config) do + if item['icon'] then + iconsPresent = true + textX = 34 + end + end + end + + -- To use the clip, set the 'mask' field of any drawable vector. + self.clip = self.scene.new('VectorClip', { units='BoundingBox' }) + local clipRect = self.clip.viewport.new('VectorRectangle', { + x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1 + }) + + self.viewport.new('VectorRectangle', { + name = 'bkgd_rect', + fill = gui.style.page.bkgd, + x = 1.5, + y = 1.5, + roundX = 3, + roundY = 3, + width = 1, + height = 1, + resizeEvent = function(Viewport, Vector, X, Y, Width, Height) + Vector.width = Width - (Vector.x * 2) + Vector.height = Height - (Vector.y * 2) + clipRect.width = Width - (Vector.x * 2) + clipRect.height = Height - (Vector.y * 2) + end + }) + + self.viewport.new('VectorRectangle', { -- Border must be the most foregound + name = 'menu_border', + stroke = gui.style.menu.stroke, + strokeWidth = 1.5, + x = 1.5, + y = 1.5, + roundX = 3, + roundY = 3, + width = 1, + height = 1, + resizeEvent = function(Viewport, Vector, X, Y, Width, Height) + Vector.width = Width - (Vector.x * 2) + Vector.height = Height - (Vector.y * 2) + end + }) + + self.items = { } + local widestItem = 0 + local widestKey = 0 + local y = 9 + for _, item in ipairs(Options.config) do + if item['item'] then + local mi = { y=y, config=item } + local vText = self.viewport.new('VectorText', { + x = textX, + face = gui.fonts.menu.face, + fontSize = string.format('%.2fpt', gui.fonts.menu.size), + fill = gui.style.page.text, + align = 'left', + string = item['item'] + }) + + local err, bx, by, bw, bh = vText.mtGetBoundary(0) + + mi.text = vText + mi.textY = y + ((vText.lineSpacing + vText.displaySize) * 0.5) + + if (bw > widestItem) then widestItem = bw end + + local icon_size = vText.displayHeight + vText.descent + + if showIcons and item['icon'] then + catch(function() + local svgGroup = self.viewport.new('VectorViewport', { + x=8, y=y, width=icon_size, height=icon_size + }) + local svg = obj.new('svg', { target=svgGroup, path='icons:' .. item['icon'] .. '.svg' }) + gui.applyFill(svgGroup, 'rgb(80,80,120,255)') + mi.svgIcon = svgGroup + end) + end + + vText.y = mi.textY + mi.height = vText.lineSpacing * ITEM_SPACING + y = y + mi.height + + table.insert(self.items, mi) + end + end + + self.surface.width = textX + widestItem + (GAP_X * 3) + widestKey + GAP_X + self.surface.height = y + end + + -- The toggle() method alternates the the visible state of the menu - for example, if the menu is hidden, it + -- will show the menu. A time-lapse feature is supported so that rapid changes to menu visibility can be avoided. + -- For example, if the TimeLapse option is set to 10 milliseconds, the menu state will not change unless the + -- specified amount of time has elapsed since the last toggle. + + local lastTime = 0 + self.toggle = function(TimeLapse) + local timelapse + if TimeLapse and (TimeLapse >= 0) then + timelapse = TimeLapse * 1000 + else + timelapse = 5000 + end + + local currentTime = mSys.PreciseTime() + if self.timeShow > self.timeHide then -- Hide the menu + if (currentTime - self.timeShow >= timelapse) then self.hide() end + lastTime = currentTime + return 0 + else + if (currentTime - self.timeHide >= timelapse) then self.show() end + lastTime = currentTime + return 1 + end + end + + self.selectItem = function() + + end + + self.getItem = function() + + end + + self.hide = function() + self.surface.acHide() + self.timeHide = mSys.PreciseTime() + end + + self.show = function() + local surface = self.surface + if bit.band(surface.flags, RNF_VISIBLE) != 0 then return end + + if self.relative then + -- Correct the position of the menu according to the relative vector that it is offset from. + + local err, target = mGfx.GetSurfaceInfo(self.relative.scene.surface) + if err == ERR_Okay then + local rel = { absX = target.absX, absY = target.absY } + local target = { absX = 0, absY = 0, height = 4096 } + + if Options.target then + local err, target = mGfx.GetSurfaceInfo(Options.target) + if err == ERR_Okay then + target.absX = target.absX + target.absY = target.absY + target.height = target.height + end + else + local err, display = mGfx.GetDisplayInfo(0) + if err == ERR_Okay then + target.height = display.height + end + end + + local x = rel.absX + self.x - target.absX + local y = rel.absY + self.y - target.absY + + if lReverseY then + y = rel.absY + nz(Options.y, 0) - target.absY - surface.height + end + + if y + surface.height > target.height then + y = y - surface.height - nz(Options.y, 0) + end + + surface.acMoveToPoint(x, y, 0, bit.bor(MTF_X, MTF_Y)) + end + end + + self.surface.acShow() + self.surface.acMoveToFront() + self.timeShow = mSys.PreciseTime() + end + + local function highlightItem(Item) + if Item == self.hItem then + return + elseif Item then + if Item.lineBreak or Item.disabled then + return + end + end + + if self.hItem then -- Redraw the previously highlighted item + self.hItem.text.fill = gui.style.page.text + self.surface.mtScheduleRedraw() + self.hItem = nil + end + + self.hItem = Item + + if Item then -- Draw the newly highlighted area + Item.text.fill = 'rgb(180,0,0)' + self.surface.mtScheduleRedraw() + end + end + + -- Main entry point -- + + if not Options.config then error('A config value is required to define the Menu items') end + + self.surface = obj.new('surface') + self.surface.name = 'menu' + self.surface.width = nz(Options.width, 400) + self.surface.parent = Options.target + self.surface.flags = bit.bor(RNF_STICK_TO_FRONT, RNF_COMPOSITE) + self.surface.x = -10000 + self.surface.y = -10000 + self.surface.windowType = 'NONE' + self.surface.height = 400 + if (self.surface.init() != ERR_Okay) then error('Failed to initialise surface.') end + + self.scene = obj.new('VectorScene', { flags=VPF_RESIZE, surface=self.surface }) + self.viewport = self.scene.new('VectorViewport', { }) + + self.surface.subscribe('free', function(Surface, Args) + self.surface = nil + end) + + self.surface.subscribe('hide', function(Surface, Args) + self.visible = false + self.timeHide = mSys.preciseTime() + end) + + self.surface.subscribe('show', function(Surface, Args) + self.visible = true + self.timeShow = mSys.preciseTime() + + -- Keyboard support requires a client reference to grab input from. + + if self.client and (not self._subscribedKb) then + if self.client.mtSubscribeKeyboard(function(Viewport, Qualifiers, Value, Unicode) + if not self.visible then + self._subscribedKb = false + check(ERR_Terminate) + end + + if bit.band(Qualifiers, KQ_PRESSED) == 0 then return end + if (Value == KEY_UP) then + elseif (Value == KEY_DOWN) then + + elseif (Value == KEY_RETURN) then + + end + end) == ERR_Okay then + self._subscribedKb = true + end + end + end) + + self.viewport.mtSubscribeInput(bit.bor(JTYPE_MOVEMENT, JTYPE_BUTTON, JTYPE_CROSSING), function(Viewport, Events) + local ev = Events + while ev do + if ev.type == JET_BUTTON_1 then + if ev.value == 1 then + if self.hItem then -- Menu item activated + if self.hItem.config.feedback then + self.hItem.config.feedback(self, self.hItem.config) + end + if self.feedback then + self.feedback(self, self.hItem.config) + end + self.surface.acHide() + end + end + elseif ev.type == JET_ABS_XY then + local highlightFound = false + for _, item in pairs(self.items) do + if not item.lineBreak then + if (ev.y >= item.y) and (ev.y < item.y + item.height) then + if self.hItem != item then + highlightItem(item) + end + highlightFound = true + break + end + end + end + + -- Remove existing menu highlighting if the cursor is no longer positioned over a highlight-able item. + + if not highlightFound and self.hItem then + highlightItem(nil) + end + end + + ev = ev.next + end + end) + + initVectorScene() + + return self +end diff --git a/scripts/gui/menu.fluid b/scripts/gui/menu.fluid index 4c5c9e566..c8a9ab8e5 100644 --- a/scripts/gui/menu.fluid +++ b/scripts/gui/menu.fluid @@ -1,28 +1,15 @@ --[[ -Documentation is available in the Parasol Wiki. +Documentation is available in the Parasol Wiki. To be used in conjunction with the gui.menubar or as an independent pop-up. --]] require 'common' require 'gui' include 'display' - if (not mGfx) then mGfx = mod.load('display') end + if (not mGfx) then mGfx = mod.load('display') end -local FONT_SIZE = 11 local ITEM_SPACING = 1.2 -local function applyFill(Vector, Fill) - while (Vector != nil) do - if (Vector.fill != 'none') then - Vector.fill = Fill - end - if (Vector.stroke != nil) then Vector.stroke = Fill end - local child = Vector.child - if (child != nil) then applyFill(child, Fill) end - Vector = Vector.next - end -end - gui.menu = function(Options) local self = { -- Public variables surface = nil, @@ -44,9 +31,7 @@ gui.menu = function(Options) local function initVectorScene() local GAP_X = 6 - local showIcons = nz(Options.showIcons, true) - local textX = GAP_X if showIcons then @@ -70,9 +55,9 @@ gui.menu = function(Options) height = 1 }) - self.vg.new('VectorRectangle', { + self.viewport.new('VectorRectangle', { name = 'bkgd_rect', - fill = 'rgb(240,240,240,255)', + fill = gui.style.page.bkgd, x = 1.5, y = 1.5, roundX = 3, @@ -87,8 +72,8 @@ gui.menu = function(Options) end }) - if showIcons and (self.style == 'menubar') or (self.style == 'popup') then - self.vg.new('VectorRectangle', { + if showIcons then + self.viewport.new('VectorRectangle', { name = 'icon_bar', fill = 'rgb(220,220,255,255)', x = 0, @@ -99,9 +84,9 @@ gui.menu = function(Options) }) end - self.vg.new('VectorRectangle', { -- Border must be the most foregound + self.viewport.new('VectorRectangle', { -- Border must be the most foregound name = 'menu_border', - stroke = 'rgb(60,60,80,180)', + stroke = gui.style.menu.stroke, strokeWidth = 1.5, x = 1.5, y = 1.5, @@ -122,11 +107,11 @@ gui.menu = function(Options) for _, item in ipairs(Options.config) do if item['item'] then local mi = { y=y, config=item } - local vText = self.vg.new('VectorText', { + local vText = self.viewport.new('VectorText', { x = textX, face = gui.fonts.menu.face, - fontSize = string.format('%.2fpt', FONT_SIZE), - fill = 'rgb(0,0,0)', + fontSize = string.format('%.2fpt', gui.fonts.menu.size), + fill = gui.style.page.text, align = 'left', string = item['item'] }) @@ -146,7 +131,7 @@ gui.menu = function(Options) x=8, y=y, width=icon_size, height=icon_size }) local svg = obj.new('svg', { target=svgGroup, path='icons:' .. item['icon'] .. '.svg' }) - applyFill(svgGroup, 'rgb(80,80,120,255)') + gui.applyFill(svgGroup, 'rgb(80,80,120,255)') mi.svgIcon = svgGroup end) end @@ -157,10 +142,10 @@ gui.menu = function(Options) key = item['qualifier'] .. ' + ' .. key end - local vKey = self.vg.new('VectorText', { + local vKey = self.viewport.new('VectorText', { x = self.surface.width, face = gui.fonts.menu.face, - fontSize = string.format('%.2fpt', FONT_SIZE), + fontSize = string.format('%.2fpt', gui.fonts.menu.size), fill = 'rgb(30,80,0,255)', align = 'right', string = key @@ -389,8 +374,8 @@ gui.menu = function(Options) end if self.hItem then -- Redraw the previously highlighted item - self.hItem.text.fill = 'rgb(0,0,0)' - self.viewport.acDraw() + self.hItem.text.fill = gui.style.page.text + self.surface.mtScheduleRedraw() self.hItem = nil end @@ -398,7 +383,7 @@ gui.menu = function(Options) if Item then -- Draw the newly highlighted area Item.text.fill = 'rgb(180,0,0)' - self.viewport.acDraw() + self.surface.mtScheduleRedraw() end end @@ -416,18 +401,17 @@ gui.menu = function(Options) self.surface = obj.new('surface') self.surface.name = 'menu' - self.surface.width = nz(Options.width, 200) + self.surface.width = nz(Options.width, 400) self.surface.parent = Options.target self.surface.flags = bit.bor(RNF_STICK_TO_FRONT, RNF_COMPOSITE) - self.surface.x = self.x - self.surface.y = self.y + self.surface.x = -10000 + self.surface.y = -10000 self.surface.windowType = 'NONE' - self.surface.height = 40 + self.surface.height = 400 if (self.surface.init() != ERR_Okay) then error('Failed to initialise surface.') end self.scene = obj.new('VectorScene', { flags=VPF_RESIZE, surface=self.surface }) self.viewport = self.scene.new('VectorViewport', { }) - self.vg = self.viewport.new('VectorGroup', { }) self.surface.subscribe('free', function(Surface, Args) if self.tipTimer then mSys.UpdateTimer(self.tipTimer, 0) end diff --git a/scripts/gui/menubar.fluid b/scripts/gui/menubar.fluid index 22f3912ec..060fc8498 100644 --- a/scripts/gui/menubar.fluid +++ b/scripts/gui/menubar.fluid @@ -11,7 +11,6 @@ Documentation is available in the Parasol Wiki. local VMARGIN = gui.style.widget.margin local GAP = 26 local ICON_SIZE = 12 -local FONT_SIZE = 16 local count = 0 local function counter() diff --git a/scripts/gui/toolbar.fluid b/scripts/gui/toolbar.fluid index 8b4244e2e..2c08d5e75 100644 --- a/scripts/gui/toolbar.fluid +++ b/scripts/gui/toolbar.fluid @@ -229,13 +229,13 @@ gui.toolbar = function(Options) end item.icon.acMoveToFront() elseif nz(item.displayName) then - local fill_name = 'tbar_text' .. counter() - gui.simpleGradient(self.viewport.scene, fill_name, gui.iconThemes[self._iconTheme], 0, 0, 0, 1) + local fill_name = 'tbar_' .. self._iconTheme + gui.simpleGradient(self.viewport.scene, fill_name, gui.iconThemes[self._iconTheme], 0, 0, 0, 1, 'boundingbox', true) item.textFill = 'url(#' .. fill_name .. ')' if self._iconHighlight and (self._iconTheme != self._iconHighlight) then - local alt_name = 'tbar_alt' .. counter() - gui.simpleGradient(self.viewport.scene, alt_name, gui.iconThemes[self._iconHighlight], 0, 0, 0, 1) + local alt_name = 'tbar_' .. self._iconHighlight + gui.simpleGradient(self.viewport.scene, alt_name, gui.iconThemes[self._iconHighlight], 0, 0, 0, 1, 'boundingbox', true) item.textAltFill = 'url(#' .. alt_name .. ')' end @@ -667,5 +667,6 @@ gui.toolbar = function(Options) arrangeItems() + collectgarbage() return self end From 7b8ca394c38d22eb64122a56500e6ce86aa2f168 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Sun, 24 Nov 2024 22:24:50 +0000 Subject: [PATCH 09/17] [Scripts] Cleanup --- scripts/gui.fluid | 28 ++++++++----- scripts/gui/combobox.fluid | 6 +-- scripts/gui/dropdown.fluid | 39 ++++++------------ scripts/gui/input.fluid | 6 +-- scripts/gui/listview.fluid | 22 +++++----- scripts/gui/menu.fluid | 84 ++++++++++++++------------------------ src/vector/scene/scene.cpp | 2 +- 7 files changed, 74 insertions(+), 113 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index bab3c61c9..29d4b994a 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -2,6 +2,8 @@ if gui then return end + if not mVec then mVec = mod.load('vector') end + gui = { theme = 'light', dpi = 160, @@ -28,20 +30,20 @@ end -- Read a style value and return it in pixel units. gui.pixel = function(val) - if (val != nil) then + if val != nil then local num, alpha = val:match('^(%d+)(%a-)$') num = tonumber(num) - if (num == nil) then error('Invalid graphics unit \'' .. val .. '\'') end - if (alpha != nil) then - if (alpha == 'px') then + if num == nil then error('Invalid graphics unit \'' .. val .. '\'') end + if alpha != nil then + if alpha == 'px' then return num - elseif (alpha == 'in') then + elseif alpha == 'in' then return gui.dpi * num - elseif (alpha == 'mm') then -- 25.4 units per inch + elseif alpha == 'mm' then -- 25.4 units per inch return gui.dpi * (num / 25.4) - elseif (alpha == 'pt') then -- 72 units per inch + elseif alpha == 'pt' then -- 72 units per inch return gui.dpi * (num / 72) - elseif (alpha == 'dp') then -- 160dpi relative value + elseif alpha == 'dp' then -- 160dpi relative value return num * (gui.dpi * 0.00625) end end @@ -61,16 +63,16 @@ gui.strToRGB = function(Value) local result xpcall(function() - if (string.byte(Value) == 35) then + if string.byte(Value) == 35 then local colour = tonumber(string.sub(Value,2), 16) - if (Value:len() == 7) then + if Value:len() == 7 then result = { r = bit.band(bit.rshift(colour, 16), 0xff), g = bit.band(bit.rshift(colour, 8), 0xff), b = bit.band(colour, 0xff), a = 255 } - elseif (Value:len() == 9) then + elseif Value:len() == 9 then result = { r = bit.band(bit.rshift(colour, 24), 0xff), g = bit.band(bit.rshift(colour, 16), 0xff), @@ -475,6 +477,10 @@ end highlight = gui.palette.contrast }, menu = { + bkgd = gui.palette.base, + stroke = gui.palette.contrast + }, + dropdown = { bkgd = gui.palette.bkgd, stroke = gui.palette.base }, diff --git a/scripts/gui/combobox.fluid b/scripts/gui/combobox.fluid index ac9ff4f3d..f549013e8 100644 --- a/scripts/gui/combobox.fluid +++ b/scripts/gui/combobox.fluid @@ -132,11 +132,7 @@ gui.combobox = function(Options) end self.bkgd = self.inputViewport.new('VectorRectangle', { - fill = gui.style.page.bkgd, - x = 0, - y = 0, - width = '100%', - height = '100%', + fill = gui.style.page.bkgd, x = 0, y = 0, width = '100%', height = '100%' }) local ICON_SIZE = FONT_HEIGHT diff --git a/scripts/gui/dropdown.fluid b/scripts/gui/dropdown.fluid index da156fd31..0722a5164 100644 --- a/scripts/gui/dropdown.fluid +++ b/scripts/gui/dropdown.fluid @@ -6,7 +6,7 @@ Internal script for combobox drop-down menus. require 'gui' include 'display' - if (not mGfx) then mGfx = mod.load('display') end + if not mGfx then mGfx = mod.load('display') end local ITEM_SPACING = 1.2 @@ -46,14 +46,8 @@ gui.dropdown = function(Options) }) self.viewport.new('VectorRectangle', { - name = 'bkgd_rect', - fill = gui.style.page.bkgd, - x = 1.5, - y = 1.5, - roundX = 3, - roundY = 3, - width = 1, - height = 1, + name = 'bkgd_rect', fill = gui.style.dropdown.bkgd, + x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) @@ -63,15 +57,8 @@ gui.dropdown = function(Options) }) self.viewport.new('VectorRectangle', { -- Border must be the most foregound - name = 'menu_border', - stroke = gui.style.menu.stroke, - strokeWidth = 1.5, - x = 1.5, - y = 1.5, - roundX = 3, - roundY = 3, - width = 1, - height = 1, + name = 'menu_border', stroke = gui.style.dropdown.stroke, + strokeWidth = 1.5, x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) @@ -99,7 +86,7 @@ gui.dropdown = function(Options) mi.text = vText mi.textY = y + ((vText.lineSpacing + vText.displaySize) * 0.5) - if (bw > widestItem) then widestItem = bw end + if bw > widestItem then widestItem = bw end local icon_size = vText.displayHeight + vText.descent @@ -126,10 +113,8 @@ gui.dropdown = function(Options) self.surface.height = y end - -- The toggle() method alternates the the visible state of the menu - for example, if the menu is hidden, it - -- will show the menu. A time-lapse feature is supported so that rapid changes to menu visibility can be avoided. - -- For example, if the TimeLapse option is set to 10 milliseconds, the menu state will not change unless the - -- specified amount of time has elapsed since the last toggle. + -- toggle() alternates the visible state of the menu. A TimeLapse is supported so that rapid changes to + -- menu visibility can be avoided. local lastTime = 0 self.toggle = function(TimeLapse) @@ -247,7 +232,7 @@ gui.dropdown = function(Options) self.surface.y = -10000 self.surface.windowType = 'NONE' self.surface.height = 400 - if (self.surface.init() != ERR_Okay) then error('Failed to initialise surface.') end + if self.surface.init() != ERR_Okay then error('Failed to initialise surface.') end self.scene = obj.new('VectorScene', { flags=VPF_RESIZE, surface=self.surface }) self.viewport = self.scene.new('VectorViewport', { }) @@ -275,10 +260,10 @@ gui.dropdown = function(Options) end if bit.band(Qualifiers, KQ_PRESSED) == 0 then return end - if (Value == KEY_UP) then - elseif (Value == KEY_DOWN) then + if Value == KEY_UP then + elseif Value == KEY_DOWN then - elseif (Value == KEY_RETURN) then + elseif Value == KEY_RETURN then end end) == ERR_Okay then diff --git a/scripts/gui/input.fluid b/scripts/gui/input.fluid index 371e711b5..559c49e1c 100644 --- a/scripts/gui/input.fluid +++ b/scripts/gui/input.fluid @@ -123,11 +123,7 @@ gui.input = function(Options) end self.bkgd = self.inputViewport.new('VectorRectangle', { - fill = gui.style.page.bkgd, - x = 0, - y = 0, - width = '100%', - height = '100%', + fill = gui.style.page.bkgd, x = 0, y = 0, width = '100%', height = '100%', }) if not Options.flushEdges then diff --git a/scripts/gui/listview.fluid b/scripts/gui/listview.fluid index e3314e4ca..2ff679b4b 100644 --- a/scripts/gui/listview.fluid +++ b/scripts/gui/listview.fluid @@ -50,16 +50,16 @@ gui.listView = function(Options) -- When an item is hovered, create a highlight for it. local function highlightItem(View, Item, Highlighted) - if (Item._vectors.viewport == nil) then return end + if Item._vectors.viewport == nil then return end - if (not Item._vectors.highlight) then + if not Item._vectors.highlight then Item._vectors.highlight = Item._vectors.viewport.new('VectorRectangle', { roundX = 4, roundY = 4, width = '100%', height = '100%', visibility = VIS_HIDDEN }) Item._vectors.highlight.acMoveToBack() end - if (Item._selected) then + if Item._selected then setListTextFill(Item, gui.style.page.textSelect) Item._vectors.highlight.fill = gui.style.page.textSelectBkgd Item._vectors.highlight.visibility = VIS_VISIBLE @@ -94,7 +94,7 @@ gui.listView = function(Options) y = y + ((#View.items - #Items) * (LINE_HEIGHT + VSPACING)) for _, item in pairs(Items) do - if (not item._vectors) then item._vectors = { } end + if not item._vectors then item._vectors = { } end item._x = x item._y = y @@ -110,7 +110,7 @@ gui.listView = function(Options) -- Respond to positive item selection by the user (e.g. button click) item._selectEvent = function(View, Item) - if (Item == View.hItem) then + if Item == View.hItem then Item._highlightEvent(View, Item, true) else Item._highlightEvent(View, Item, false) @@ -147,7 +147,7 @@ gui.listView = function(Options) end if gui.style.page.iconSelect then - if (gui.style.page.iconSelect == gui.style.page.icon) then + if gui.style.page.iconSelect == gui.style.page.icon then View.sIcons[item.icon] = item.icon else gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconSelect, '_s_' .. item.icon) @@ -156,9 +156,9 @@ gui.listView = function(Options) end if gui.style.page.iconHighlight then - if (gui.style.page.iconHighlight == gui.style.page.icon) then + if gui.style.page.iconHighlight == gui.style.page.icon then View.hIcons[item.icon] = item.icon - elseif (gui.style.page.iconHighlight != gui.style.page.iconSelect) then + elseif gui.style.page.iconHighlight != gui.style.page.iconSelect then gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconHighlight, '_s_' .. item.icon) View.hIcons[item.icon] = '_s_' .. item.icon else @@ -192,7 +192,7 @@ gui.listView = function(Options) item._vectors.text.x = MARGIN + ICON_MARGIN item._vectors.text.y = LINE_OFFSET - if (type(item_name) == 'table') then + if type(item_name) == 'table' then item._vectors.text.string = item_name.value else item._vectors.text.string = item_name @@ -219,14 +219,14 @@ gui.listView = function(Options) y = y + LINE_HEIGHT + VSPACING end - if (width > widest_item) then widest_item = width end + if width > widest_item then widest_item = width end end -- Item click and hover management for _, item in pairs(View.items) do if not item.hidden then - if (Options.compact == 1) then + if Options.compact == 1 then View.addZone(item._x, item._y, widest_item, item._height, item, itemHoverCallback) else View.addZone(item._x, item._y, nil, item._height, item, itemHoverCallback) diff --git a/scripts/gui/menu.fluid b/scripts/gui/menu.fluid index c8a9ab8e5..8c0affcd3 100644 --- a/scripts/gui/menu.fluid +++ b/scripts/gui/menu.fluid @@ -6,7 +6,7 @@ Documentation is available in the Parasol Wiki. To be used in conjunction with require 'gui' include 'display' - if (not mGfx) then mGfx = mod.load('display') end + if not mGfx then mGfx = mod.load('display') end local ITEM_SPACING = 1.2 @@ -47,23 +47,13 @@ gui.menu = function(Options) -- To use the clip, set the 'mask' field of any drawable vector. self.clip = self.scene.new('VectorClip', { units='BoundingBox' }) local clipRect = self.clip.viewport.new('VectorRectangle', { - x = 1.5, - y = 1.5, - roundX = 3, - roundY = 3, - width = 1, - height = 1 + x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1 }) self.viewport.new('VectorRectangle', { - name = 'bkgd_rect', - fill = gui.style.page.bkgd, - x = 1.5, - y = 1.5, - roundX = 3, - roundY = 3, - width = 1, - height = 1, + name = 'bkgd_rect', + fill = gui.menu.bkgd, + x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) @@ -74,26 +64,16 @@ gui.menu = function(Options) if showIcons then self.viewport.new('VectorRectangle', { - name = 'icon_bar', - fill = 'rgb(220,220,255,255)', - x = 0, - y = 0, - width = 30, - height = '100%', - mask = self.clip + name = 'icon_bar', + fill = 'rgb(220,220,255,255)', + x = 0, y = 0, width = 30, height = '100%', mask = self.clip }) end self.viewport.new('VectorRectangle', { -- Border must be the most foregound name = 'menu_border', - stroke = gui.style.menu.stroke, - strokeWidth = 1.5, - x = 1.5, - y = 1.5, - roundX = 3, - roundY = 3, - width = 1, - height = 1, + stroke = gui.style.menu.stroke, strokeWidth = 1.5, + x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) @@ -121,7 +101,7 @@ gui.menu = function(Options) mi.text = vText mi.textY = y + ((vText.lineSpacing + vText.displaySize) * 0.5) - if (bw > widestItem) then widestItem = bw end + if bw > widestItem then widestItem = bw end local icon_size = vText.displayHeight + vText.descent @@ -156,7 +136,7 @@ gui.menu = function(Options) vKey.y = y + ((vKey.lineSpacing + vKey.displaySize) * 0.5) keyWidth = bw - if (keyWidth > widestKey) then widestKey = keyWidth end + if keyWidth > widestKey then widestKey = keyWidth end mi.keyText = vKey end @@ -178,10 +158,8 @@ gui.menu = function(Options) end end - -- The toggle() method alternates the the visible state of the menu - for example, if the menu is hidden, it - -- will show the menu. A time-lapse feature is supported so that rapid changes to menu visibility can be avoided. - -- For example, if the TimeLapse option is set to 10 milliseconds, the menu state will not change unless the - -- specified amount of time has elapsed since the last toggle. + -- toggle() alternates the visible state of the menu. A TimeLapse is supported so that rapid changes to + -- menu visibility can be avoided. local lastTime = 0 self.toggle = function(TimeLapse) @@ -193,7 +171,7 @@ gui.menu = function(Options) end local currentTime = mSys.PreciseTime() - if (self.timeShow > self.timeHide) then -- Hide the menu + if self.timeShow > self.timeHide then -- Hide the menu if (currentTime - self.timeShow >= timelapse) then self.hide() end lastTime = currentTime return 0 @@ -225,13 +203,13 @@ gui.menu = function(Options) if self.popup then local err, cursor_x, cursor_y = mGfx.getCursorPos() - if (err == ERR_Okay) then + if err == ERR_Okay then local parent = { } local sp = surface.parent if sp then err, parentInfo = mGfx.GetSurfaceInfo(sp) - if (err == ERR_Okay) then + if err == ERR_Okay then parent.width = parentInfo.width parent.height = parentInfo.height parent.absX = parentInfo.absX @@ -261,7 +239,7 @@ gui.menu = function(Options) local x if self.reverseX then x = cursor_x - parent.absX - 1 - surface.width + gui.style.widget.margin - if (x < 0) then + if x < 0 then x = 0 self.reverseX = false end @@ -285,7 +263,7 @@ gui.menu = function(Options) --[[ local parent = { x=0, y = 0, width = 0 } local parent = self.parentMenu - if (parent.menuSurfaceID) then + if parent.menuSurfaceID then local err, info = mGfx.GetSurfaceInfo(parent.MenuSurfaceID) if err == ERR_Okay then parent.x = info.x @@ -296,7 +274,7 @@ gui.menu = function(Options) parent.currentMenu = self local x = parent.x + parent.width - gui.style.widget.margin - if (self.reverseX) then + if self.reverseX then x = parent.x - surface.width + gui.style.widget.margin if x < 2 then x = 2 @@ -304,7 +282,7 @@ gui.menu = function(Options) end else SURFACEINFO *target - if (Options.target) and (!mGfx.GetSurfaceInfo(Options.target, &target)) then + if Options.target and (!mGfx.GetSurfaceInfo(Options.target, &target)) then -- A specific target surface is hosting the menu layer adjust the coordinate if necessary to keep -- it from being partially hidden. if x + surface.width >= target.width then @@ -328,14 +306,14 @@ gui.menu = function(Options) if Options.target then local err, target = mGfx.GetSurfaceInfo(Options.target) - if (err == ERR_Okay) then + if err == ERR_Okay then target.absX = target.absX target.absY = target.absY target.height = target.height end else local err, display = mGfx.GetDisplayInfo(0) - if (err == ERR_Okay) then + if err == ERR_Okay then target.height = display.height end end @@ -388,7 +366,7 @@ gui.menu = function(Options) end local function timer(Elapsed, CurrentTime) - if (lCurrentItem) and nz(lCurrentItem.tip) then + if lCurrentItem and nz(lCurrentItem.tip) then gui.tooltip({ text=lCurrentItem.tip }) end self.tipTimer = nil @@ -408,7 +386,7 @@ gui.menu = function(Options) self.surface.y = -10000 self.surface.windowType = 'NONE' self.surface.height = 400 - if (self.surface.init() != ERR_Okay) then error('Failed to initialise surface.') end + if self.surface.init() != ERR_Okay then error('Failed to initialise surface.') end self.scene = obj.new('VectorScene', { flags=VPF_RESIZE, surface=self.surface }) self.viewport = self.scene.new('VectorViewport', { }) @@ -446,10 +424,10 @@ gui.menu = function(Options) end if bit.band(Qualifiers, KQ_PRESSED) == 0 then return end - if (Value == KEY_UP) then - elseif (Value == KEY_DOWN) then + if Value == KEY_UP then + elseif Value == KEY_DOWN then - elseif (Value == KEY_RETURN) then + elseif Value == KEY_RETURN then end end) == ERR_Okay then @@ -461,7 +439,7 @@ gui.menu = function(Options) self.viewport.mtSubscribeInput(bit.bor(JTYPE_MOVEMENT, JTYPE_BUTTON, JTYPE_CROSSING), function(Viewport, Events) local ev = Events while ev do - if (ev.type == JET_BUTTON_1) then + if ev.type == JET_BUTTON_1 then if ev.value == 1 then if self.hItem then -- Menu item activated if self.hItem.config.feedback then @@ -473,12 +451,12 @@ gui.menu = function(Options) self.surface.acHide() end end - elseif (ev.type == JET_ABS_XY) then + elseif ev.type == JET_ABS_XY then local highlightFound = false for _, item in pairs(self.items) do if not item.lineBreak then if (ev.y >= item.y) and (ev.y < item.y + item.height) then - if (self.hItem != item) then + if self.hItem != item then highlightItem(item) end highlightFound = true diff --git a/src/vector/scene/scene.cpp b/src/vector/scene/scene.cpp index c42261428..a84b3f7c6 100644 --- a/src/vector/scene/scene.cpp +++ b/src/vector/scene/scene.cpp @@ -12,7 +12,7 @@ The VectorScene class acts as a container and control point for the management o purpose is to draw the scene to a target @Bitmap or @Surface provided by the client. Vector scenes are created by initialising multiple Vector objects such as @VectorPath and @VectorViewport and -positioning them within a vector tree. The VectorScene must lie at the root. +positioning them within a vector tree. The VectorScene must lie at the root of the tree. The default mode of operation is for scenes to be manually drawn, for which the client must set the target #Bitmap and call the #Draw() action as required. Automated drawing can be enabled by setting the target #Surface prior From f741e5f301445edf211d17eadeb08f235fb9b29d Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Mon, 25 Nov 2024 18:12:01 +0000 Subject: [PATCH 10/17] [Scripts] Reworked dropdown and menu scripts --- scripts/gui.fluid | 23 +++++--- scripts/gui/dropdown.fluid | 71 ++++++++++++++++-------- scripts/gui/fileview.fluid | 2 +- scripts/gui/fontdialog.fluid | 11 ++-- scripts/gui/menu.fluid | 102 ++++++++++++++++++----------------- 5 files changed, 123 insertions(+), 86 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 29d4b994a..1034ea6b7 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -428,7 +428,7 @@ end contrast = '#e89214', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - sunrise = { type = 'light', bkgd = '#ECF0F1', base = '#687878', prime = '#e2581b', second = '#bd3232', + sunrise = { type = 'light', bkgd = '#f9FfFc', base = '#687878', prime = '#e2581b', second = '#bd3232', contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, @@ -478,11 +478,16 @@ end }, menu = { bkgd = gui.palette.base, - stroke = gui.palette.contrast + stroke = gui.palette.neutral, + text = gui.palette.altText }, dropdown = { bkgd = gui.palette.bkgd, - stroke = gui.palette.base + stroke = gui.palette.base, + icon = nil, + iconHighlight = nil, + text = gui.palette.text, + textHighlight = gui.palette.altText }, desktop = { bkgd = 'rgb(160,160,160)' @@ -490,16 +495,22 @@ end } if gui.palette.type == 'light' then - gui.style.page.icon = 'carbon' + gui.style.page.icon = 'carbon' gui.style.page.iconHighlight = 'pearl' gui.style.page.iconSelect = 'pearl' gui.style.page.text = gui.palette.text gui.style.page.textHighlight = gui.palette.altText gui.style.page.textSelect = gui.palette.altText + gui.style.menu.icon = 'pearl' + gui.style.dropdown.icon = 'carbon' + gui.style.dropdown.iconHighlight = 'pearl' else - gui.style.page.icon = 'pearl' + gui.style.page.icon = 'pearl' gui.style.page.iconHighlight = 'carbon' - gui.style.page.iconSelect = 'carbon' + gui.style.page.iconSelect = 'carbon' + gui.style.menu.icon = 'carbon' + gui.style.dropdown.icon = 'pearl' + gui.style.dropdown.iconHighlight = 'carbon' end gui.interface = { diff --git a/scripts/gui/dropdown.fluid b/scripts/gui/dropdown.fluid index 0722a5164..a3cedeaea 100644 --- a/scripts/gui/dropdown.fluid +++ b/scripts/gui/dropdown.fluid @@ -8,6 +8,11 @@ Internal script for combobox drop-down menus. if not mGfx then mGfx = mod.load('display') end +local FONT_MAX_HEIGHT, FONT_HEIGHT = gui.getFontHeight(gui.fonts.menu) +local ITEM_HEIGHT = math.floor(FONT_MAX_HEIGHT * 1.5) +local VMARGIN = math.floor(ITEM_HEIGHT * 0.33) +local HMARGIN = 6 +local ICON_SIZE = math.floor(ITEM_HEIGHT * 0.8) local ITEM_SPACING = 1.2 gui.dropdown = function(Options) @@ -25,16 +30,15 @@ gui.dropdown = function(Options) } local function initVectorScene() - local GAP_X = 6 local showIcons = nz(Options.showIcons, true) - local textX = GAP_X + local TEXT_X = HMARGIN if showIcons then local iconsPresent = false for _, item in ipairs(Options.config) do - if item['icon'] then + if item.icon then iconsPresent = true - textX = 34 + TEXT_X = HMARGIN + ICON_SIZE + HMARGIN + HMARGIN end end end @@ -45,8 +49,8 @@ gui.dropdown = function(Options) x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1 }) - self.viewport.new('VectorRectangle', { - name = 'bkgd_rect', fill = gui.style.dropdown.bkgd, + self.viewport.new('VectorRectangle', { -- Background + fill = gui.style.dropdown.bkgd, x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) @@ -56,8 +60,12 @@ gui.dropdown = function(Options) end }) - self.viewport.new('VectorRectangle', { -- Border must be the most foregound - name = 'menu_border', stroke = gui.style.dropdown.stroke, + self.highlight = self.viewport.new('VectorRectangle', { + x = 2, y = 0, xOffset = 2, height = ITEM_HEIGHT, fill = gui.palette.contrast, visibility = 'hidden' + }) + + self.viewport.new('VectorRectangle', { -- Border + stroke = gui.style.dropdown.stroke, strokeWidth = 1.5, x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) @@ -68,12 +76,12 @@ gui.dropdown = function(Options) self.items = { } local widestItem = 0 local widestKey = 0 - local y = 9 + local y = VMARGIN for _, item in ipairs(Options.config) do if item['item'] then local mi = { y=y, config=item } local vText = self.viewport.new('VectorText', { - x = textX, + x = TEXT_X, face = gui.fonts.menu.face, fontSize = string.format('%.2fpt', gui.fonts.menu.size), fill = gui.style.page.text, @@ -84,20 +92,23 @@ gui.dropdown = function(Options) local err, bx, by, bw, bh = vText.mtGetBoundary(0) mi.text = vText - mi.textY = y + ((vText.lineSpacing + vText.displaySize) * 0.5) + mi.textY = y + ((vText.lineSpacing + vText.displayHeight) * 0.5) if bw > widestItem then widestItem = bw end - local icon_size = vText.displayHeight + vText.descent + if showIcons and item.icon then + pcall(function() + local info = gui.createIcon(self.viewport.scene, item.icon, ICON_SIZE, gui.style.dropdown.icon) + mi.iconFill = info.fill + + if gui.style.dropdown.iconHighlight and gui.style.dropdown.iconHighlight != gui.style.dropdown.icon then + local info = gui.createIcon(self.viewport.scene, item.icon, ICON_SIZE, gui.style.dropdown.iconHighlight) + mi.iconHighlightFill = info.fill + end - if showIcons and item['icon'] then - catch(function() - local svgGroup = self.viewport.new('VectorViewport', { - x=8, y=y, width=icon_size, height=icon_size + mi.iconView = self.viewport.new('VectorRectangle', { + x = HMARGIN, y = y + math.floor((ITEM_HEIGHT - ICON_SIZE) * 0.5), width = ICON_SIZE, height = ICON_SIZE, fill = info.fill }) - local svg = obj.new('svg', { target=svgGroup, path='icons:' .. item['icon'] .. '.svg' }) - gui.applyFill(svgGroup, 'rgb(80,80,120,255)') - mi.svgIcon = svgGroup end) end @@ -109,8 +120,8 @@ gui.dropdown = function(Options) end end - self.surface.width = textX + widestItem + (GAP_X * 3) + widestKey + GAP_X - self.surface.height = y + self.surface.width = TEXT_X + widestItem + (HMARGIN * 3) + widestKey + HMARGIN + self.surface.height = y + VMARGIN end -- toggle() alternates the visible state of the menu. A TimeLapse is supported so that rapid changes to @@ -206,7 +217,10 @@ gui.dropdown = function(Options) end if self.hItem then -- Redraw the previously highlighted item - self.hItem.text.fill = gui.style.page.text + self.hItem.text.fill = gui.style.dropdown.text + if self.hItem.iconView then + self.hItem.iconView.fill = self.hItem.iconFill + end self.surface.mtScheduleRedraw() self.hItem = nil end @@ -214,7 +228,16 @@ gui.dropdown = function(Options) self.hItem = Item if Item then -- Draw the newly highlighted area - Item.text.fill = 'rgb(180,0,0)' + Item.text.fill = gui.style.dropdown.textHighlight + if Item.iconView then + Item.iconView.fill = Item.iconHighlightFill + end + self.highlight.y = Item.y + self.highlight.height = Item.height + self.highlight.visibility = 'visible' + self.surface.mtScheduleRedraw() + else + self.highlight.visibility = 'hidden' self.surface.mtScheduleRedraw() end end @@ -306,6 +329,8 @@ gui.dropdown = function(Options) if not highlightFound and self.hItem then highlightItem(nil) end + elseif (ev.type == JET_CROSSED_OUT) then + if self.hItem then highlightItem(nil) end end ev = ev.next diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index c7c062fe0..6786d4367 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -1509,7 +1509,7 @@ gui.fileview = function(View, Options) end Bar.addItem(3, 'Bookmark', 'Create Bookmark', 'items/new_bookmark', function() - if (string.len(self.path) < 3) then return end + if not self.path or (string.len(self.path) < 3) then return end local current_path = self.path diff --git a/scripts/gui/fontdialog.fluid b/scripts/gui/fontdialog.fluid index 24efb2ebe..fb5df9027 100644 --- a/scripts/gui/fontdialog.fluid +++ b/scripts/gui/fontdialog.fluid @@ -126,7 +126,7 @@ Fusce auctor metus egestas commodo laoreet. Morbi sed venenatis augue. Maecenas lFontsSorted = { } local font = list local index = 0 - while (font) do + while font do lFonts[font.name] = { } lFonts[font.name].order = index lFonts[font.name].name = font.name @@ -265,13 +265,8 @@ Fusce auctor metus egestas commodo laoreet. Morbi sed venenatis augue. Maecenas }) self.exampleBkgd = self.exampleVP.new('VectorRectangle', { - fill = 'rgb(253,255,246)', - stroke = 'rgb(0,0,0,64)', - strokeWidth = 2, - x = 0, - y = 0, - width = '100%', - height = '100%' + fill = 'rgb(253,255,246)', stroke = 'rgb(0,0,0,64)', strokeWidth = 2, + x = 0, y = 0, width = '100%', height = '100%' }) self.exampleView = self.exampleVP.new('VectorViewport', { diff --git a/scripts/gui/menu.fluid b/scripts/gui/menu.fluid index 8c0affcd3..a713b854a 100644 --- a/scripts/gui/menu.fluid +++ b/scripts/gui/menu.fluid @@ -1,5 +1,6 @@ --[[ -Documentation is available in the Parasol Wiki. To be used in conjunction with the gui.menubar or as an independent pop-up. +Documentation is available in the Parasol Wiki. +To be used in conjunction with the gui.menubar or as an independent pop-up only. --]] require 'common' @@ -8,7 +9,13 @@ Documentation is available in the Parasol Wiki. To be used in conjunction with if not mGfx then mGfx = mod.load('display') end -local ITEM_SPACING = 1.2 +local FONT_MAX_HEIGHT, FONT_HEIGHT = gui.getFontHeight(gui.fonts.menu) +local ITEM_HEIGHT = math.floor(FONT_MAX_HEIGHT * 1.5) +local VMARGIN = math.floor(ITEM_HEIGHT * 0.33) +local HMARGIN = 6 +local ICON_SIZE = math.floor(ITEM_HEIGHT * 0.8) +local KB_HIGHLIGHT = 'rgb(0,0,0,120)' +local ICON_STRIP = 'rgb(255,255,255,40)' gui.menu = function(Options) local self = { -- Public variables @@ -30,50 +37,40 @@ gui.menu = function(Options) } local function initVectorScene() - local GAP_X = 6 local showIcons = nz(Options.showIcons, true) - local textX = GAP_X + local TEXT_X = HMARGIN if showIcons then local iconsPresent = false for _, item in ipairs(Options.config) do if item['icon'] then iconsPresent = true - textX = 34 + TEXT_X = HMARGIN + ICON_SIZE + HMARGIN + HMARGIN end end end - -- To use the clip, set the 'mask' field of any drawable vector. - self.clip = self.scene.new('VectorClip', { units='BoundingBox' }) - local clipRect = self.clip.viewport.new('VectorRectangle', { - x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1 - }) - self.viewport.new('VectorRectangle', { - name = 'bkgd_rect', - fill = gui.menu.bkgd, - x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, + fill = gui.style.menu.bkgd, x = 0, y = 0, xOffset = 0, yOffset = 0, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) - clipRect.width = Width - (Vector.x * 2) - clipRect.height = Height - (Vector.y * 2) end }) - if showIcons then + if showIcons then -- Icon strip self.viewport.new('VectorRectangle', { - name = 'icon_bar', - fill = 'rgb(220,220,255,255)', - x = 0, y = 0, width = 30, height = '100%', mask = self.clip + fill = ICON_STRIP, x = 0, y = 0, width = HMARGIN + ICON_SIZE + HMARGIN, height = '100%' }) end - self.viewport.new('VectorRectangle', { -- Border must be the most foregound - name = 'menu_border', - stroke = gui.style.menu.stroke, strokeWidth = 1.5, - x = 1.5, y = 1.5, roundX = 3, roundY = 3, width = 1, height = 1, + self.highlight = self.viewport.new('VectorRectangle', { + x = 2, y = 0, xOffset = 2, height = ITEM_HEIGHT, fill = gui.palette.contrast, visibility = 'hidden' + }) + + self.viewport.new('VectorRectangle', { -- Border + stroke = gui.style.menu.stroke, strokeWidth = 1.5, opacity=0.5, + x = 1, y = 1, xOffset = 1, yOffset = 1, resizeEvent = function(Viewport, Vector, X, Y, Width, Height) Vector.width = Width - (Vector.x * 2) Vector.height = Height - (Vector.y * 2) @@ -83,15 +80,15 @@ gui.menu = function(Options) self.items = { } local widestItem = 0 local widestKey = 0 - local y = 9 + local y = VMARGIN for _, item in ipairs(Options.config) do if item['item'] then local mi = { y=y, config=item } local vText = self.viewport.new('VectorText', { - x = textX, + x = TEXT_X, face = gui.fonts.menu.face, fontSize = string.format('%.2fpt', gui.fonts.menu.size), - fill = gui.style.page.text, + fill = gui.style.menu.text, align = 'left', string = item['item'] }) @@ -99,20 +96,16 @@ gui.menu = function(Options) local err, bx, by, bw, bh = vText.mtGetBoundary(0) mi.text = vText - mi.textY = y + ((vText.lineSpacing + vText.displaySize) * 0.5) + mi.textY = y + math.floor((vText.lineSpacing + vText.displayHeight) * 0.5) if bw > widestItem then widestItem = bw end - local icon_size = vText.displayHeight + vText.descent - if showIcons and item['icon'] then - catch(function() - local svgGroup = self.viewport.new('VectorViewport', { - x=8, y=y, width=icon_size, height=icon_size + pcall(function() + local info = gui.createIcon(self.viewport.scene, item.icon, ICON_SIZE, gui.style.menu.icon) + item.iconView = self.viewport.new('VectorRectangle', { + x = HMARGIN, y = y + math.floor((ITEM_HEIGHT - ICON_SIZE) * 0.5), width = ICON_SIZE, height = ICON_SIZE, fill = info.fill }) - local svg = obj.new('svg', { target=svgGroup, path='icons:' .. item['icon'] .. '.svg' }) - gui.applyFill(svgGroup, 'rgb(80,80,120,255)') - mi.svgIcon = svgGroup end) end @@ -122,38 +115,46 @@ gui.menu = function(Options) key = item['qualifier'] .. ' + ' .. key end + local kb_highlight = self.viewport.new('VectorRectangle', { + fill = KB_HIGHLIGHT, xOffset = HMARGIN, y = y + 2, width = 1, height = ITEM_HEIGHT - 4, + roundX = ITEM_HEIGHT * 0.5, roundY = ITEM_HEIGHT * 0.5 + }) + local vKey = self.viewport.new('VectorText', { x = self.surface.width, face = gui.fonts.menu.face, fontSize = string.format('%.2fpt', gui.fonts.menu.size), - fill = 'rgb(30,80,0,255)', + fill = gui.style.menu.text, align = 'right', string = key }) local err, bx, by, bw, bh = vKey.mtGetBoundary(0) - vKey.y = y + ((vKey.lineSpacing + vKey.displaySize) * 0.5) + vKey.y = y + math.floor((vKey.lineSpacing + vKey.displayHeight) * 0.5) keyWidth = bw if keyWidth > widestKey then widestKey = keyWidth end mi.keyText = vKey + kb_highlight.width = keyWidth + HMARGIN + HMARGIN end vText.y = mi.textY - mi.height = vText.lineSpacing * ITEM_SPACING + mi.height = ITEM_HEIGHT y = y + mi.height table.insert(self.items, mi) end end - self.surface.width = textX + widestItem + (GAP_X * 3) + widestKey + GAP_X - self.surface.height = y + local width = TEXT_X + widestItem + (HMARGIN * 6) + widestKey + HMARGIN + + self.surface.width = width + self.surface.height = y + VMARGIN for _, item in pairs(self.items) do if item['keyText'] then - item.keyText.x = self.surface.width - GAP_X + item.keyText.x = self.surface.width - HMARGIN - HMARGIN end end end @@ -327,9 +328,9 @@ gui.menu = function(Options) if (y + surface.height > target.height) then if self.parentMenu then -- Child menu - y = y - surface.height --+ get_item_height() + self.vWhiteSpace + y = y - surface.height else - y = y - surface.height - nz(Options.y, 0) --+ self.vWhiteSpace + y = y - surface.height - nz(Options.y, 0) end end @@ -352,7 +353,6 @@ gui.menu = function(Options) end if self.hItem then -- Redraw the previously highlighted item - self.hItem.text.fill = gui.style.page.text self.surface.mtScheduleRedraw() self.hItem = nil end @@ -360,7 +360,12 @@ gui.menu = function(Options) self.hItem = Item if Item then -- Draw the newly highlighted area - Item.text.fill = 'rgb(180,0,0)' + self.highlight.y = Item.y + self.highlight.height = Item.height + self.highlight.visibility = 'visible' + self.surface.mtScheduleRedraw() + else + self.highlight.visibility = 'hidden' self.surface.mtScheduleRedraw() end end @@ -475,10 +480,11 @@ gui.menu = function(Options) if self.tipTimer then mSys.UpdateTimer(self.tipTimer, self.tipDelay) else - local err - err, self.tipTimer = mSys.SubscribeTimer(self.tipDelay, timer) + _, self.tipTimer = mSys.SubscribeTimer(self.tipDelay, timer) end end + elseif (ev.type == JET_CROSSED_OUT) then + if self.hItem then highlightItem(nil) end end ev = ev.next From 2fb8e0857a249d69e8bcd77ea31f24ae334afa59 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Mon, 25 Nov 2024 18:12:51 +0000 Subject: [PATCH 11/17] [Vector] Fixed issue with crossing events not being reported to vectors. --- src/vector/scene/scene_input.cpp | 2 +- src/vector/vectors/vector.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vector/scene/scene_input.cpp b/src/vector/scene/scene_input.cpp index 334d63d01..73ce3fef1 100644 --- a/src/vector/scene/scene_input.cpp +++ b/src/vector/scene/scene_input.cpp @@ -187,7 +187,7 @@ ERR scene_input_events(const InputEvent *Events, LONG Handle) } } - if (input->OverID != Self->SurfaceID) { + if ((input->OverID != Self->SurfaceID) and ((input->Flags & JTYPE::CROSSING) IS JTYPE::NIL)) { // Activity occurring on another surface may be reported to us in circumstances where our surface is modal. continue; } diff --git a/src/vector/vectors/vector.cpp b/src/vector/vectors/vector.cpp index f20445a38..579535b3a 100644 --- a/src/vector/vectors/vector.cpp +++ b/src/vector/vectors/vector.cpp @@ -901,6 +901,8 @@ static ERR VECTOR_SubscribeInput(extVector *Self, struct vec::SubscribeInput *Ar { pf::Log log; + // Refer to scene_input_events() for the origin of incoming input messages + if ((!Args) or (!Args->Callback)) return log.warning(ERR::NullArgs); if (Args->Mask != JTYPE::NIL) { From d713114120b90b13a168914d1704640dc307c3da Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Tue, 26 Nov 2024 11:58:56 +0000 Subject: [PATCH 12/17] [Scripts] Cleanup --- scripts/gui.fluid | 54 ++++++++--------- scripts/gui/columnview.fluid | 111 +++++++++++++++-------------------- scripts/gui/filedialog.fluid | 16 +++-- scripts/gui/fileview.fluid | 80 ++++++++++++------------- scripts/gui/toolbar.fluid | 2 +- 5 files changed, 127 insertions(+), 136 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 1034ea6b7..234195ac5 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -91,14 +91,14 @@ gui.strToRGB = function(Value) local g = nz(colours[2], 0) local b = nz(colours[3], 0) local a = nz(colours[4], 255) - if (r > 255) then r = 255 end - if (g > 255) then g = 255 end - if (b > 255) then b = 255 end - if (a > 255) then a = 255 end - if (r < 0) then r = 0 end - if (g < 0) then g = 0 end - if (b < 0) then b = 0 end - if (a < 0) then a = 0 end + if r > 255 then r = 255 end + if g > 255 then g = 255 end + if b > 255 then b = 255 end + if a > 255 then a = 255 end + if r < 0 then r = 0 end + if g < 0 then g = 0 end + if b < 0 then b = 0 end + if a < 0 then a = 0 end result = { r=r, g=g, b=b, a=a } end end, @@ -234,7 +234,7 @@ gui.forVector = function(Vector, Function) while (Vector != nil) do Function(Vector) local child = Vector.child - if (child != nil) then gui.forVector(child, Function) end + if child != nil then gui.forVector(child, Function) end Vector = Vector.next end end @@ -243,12 +243,12 @@ end gui.applyFill = function(Vector, Fill) while (Vector != nil) do - if (Vector.fill != 'none') then + if Vector.fill != 'none' then Vector.fill = Fill end - if (Vector.stroke != nil) then Vector.stroke = Fill end + if Vector.stroke != nil then Vector.stroke = Fill end local child = Vector.child - if (child != nil) then gui.applyFill(child, Fill) end + if child != nil then gui.applyFill(child, Fill) end Vector = Vector.next end end @@ -345,11 +345,11 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) icon.svg = obj.new('svg', { target=icon.pattern.viewport, path=Path }) gui.forVector(icon.pattern.viewport, function(Vector) - if (Vector.fill != 'none') then + if Vector.fill != 'none' then Vector.fill = 'url(#' .. gname .. ')' end - if (Vector.stroke != nil) then + if Vector.stroke != nil then Vector.stroke = 'url(#' .. gname .. ')' elseif lGradient.stroke then Vector.stroke = gui.rgbToSVG(lGradient.stroke) @@ -360,15 +360,15 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) -- Adjust gradient size to match the viewport height. local viewport = icon.pattern.viewport.child - if (viewport != nil) then - while (child != nil) do - if (child.class.id == ID_VECTORVIEWPORT) then + if viewport != nil then + while child != nil do + if child.class.id == ID_VECTORVIEWPORT then viewport = child break end end - if (viewport.viewHeight < viewport.viewWidth) then + if viewport.viewHeight < viewport.viewWidth then icon.gradient.y1 = viewport.viewY - ((viewport.viewWidth - viewport.viewHeight) * 0.5) icon.gradient.y2 = icon.gradient.y1 + viewport.viewWidth else @@ -540,21 +540,21 @@ end local xml = obj.new('xml', { path=path }) - if (xml != nil) then + if xml != nil then --[[ local err, index = xml.mtFindTag('/colours') - if (err == ERR_Okay) then + if err == ERR_Okay then for k,v in pairs(gui.style) do err, colour = xml.mtGetAttrib(index, k) - if (err == ERR_Okay) then gui.style[k] = colour end + if err == ERR_Okay then gui.style[k] = colour end end end --]] local err, index = xml.mtFindTag('/interface') - if (err == ERR_Okay) then + if err == ERR_Okay then for k,v in pairs(gui.interface) do err, val = xml.mtGetAttrib(index, k) - if (err == ERR_Okay) then + if err == ERR_Okay then if string.match(val, '^%-?%d+$') then gui.interface[k] = tonumber(val) else @@ -567,14 +567,14 @@ end convertFontSizes() local err, index = xml.mtFindTag('/fonts') - if (err == ERR_Okay) then + if err == ERR_Okay then for k,v in pairs(gui.fonts) do local err, index = xml.mtFindTag("/fonts/font(@name='" .. k .. "')") - if (err == ERR_Okay) then + if err == ERR_Okay then local err, face = xml.mtGetAttrib(index, 'face') - if (err == ERR_Okay) then gui.fonts[k].face = face end + if err == ERR_Okay then gui.fonts[k].face = face end local err, size = xml.mtGetAttrib(index, 'size') - if (err == ERR_Okay) then + if err == ERR_Okay then if string.match(size, '^%-?%d+$') then gui.fonts[k].size = tonumber(size) elseif string.match(size, '^%-?%d+%%$') then diff --git a/scripts/gui/columnview.fluid b/scripts/gui/columnview.fluid index 0343535b8..f37befc97 100644 --- a/scripts/gui/columnview.fluid +++ b/scripts/gui/columnview.fluid @@ -22,13 +22,7 @@ local LINE_OFFSET = (LINE_HEIGHT + FONT_HEIGHT) * 0.5 local BANNER_HEIGHT = math.round(LINE_HEIGHT * 1.15) local lColours = { - buttonBackground = 'rgb(240,240,240)', - fillTitleFont = 'rgb(0,0,0)', - fillTitleSortFont = 'rgb(0,0,0)', - fillSelect = 'rgb(0,0,60,140)', - fillBkgdHighlight = 'rgb(0,0,0,10)', - fillBkgdSelectHighlight = 'rgb(0,0,90,140)', - strokeButton = 'rgb(160,160,160)' + strokeButton = 'rgb(160,160,160)' } ---------------------------------------------------------------------------------------------------------------------- @@ -38,55 +32,51 @@ local function columnText(Value, Type) Value = Value.value end - if not Value then - return '' - end + if not Value then return '' end - if (Type == 'bytesize') then + if Type == 'bytesize' then Value = tonumber(Value) if not Value then return '' - elseif (Value < 1024) then + elseif Value < 1024 then return string.format('%.0f', Value) - elseif (Value < 1024 * 1024) then + elseif Value < 1024 * 1024 then return string.format('%.0f KB', Value / 1024) - elseif (Value < 1024 * 1024 * 1024) then + elseif Value < 1024 * 1024 * 1024 then Value = Value / (1024 * 1024) - if (Value >= 10) then + if Value >= 10 then return string.format('%.0f MB', Value) else return string.format('%.1f MB', Value) end - elseif (Value < 1024 * 1024 * 1024 * 1024) then + elseif Value < 1024 * 1024 * 1024 * 1024 then return string.format('%.1f GB', Value / (1024 * 1024 * 1024)) else return string.format('%.1f TB', Value / (1024 * 1024 * 1024 * 1024)) end - elseif (Type == 'date') then - -- The date type can be expressed as "YYYY-MM-DD HH:NN", "YYYY-MM-DD", "YYYY-MM-DDTHH:NN". - - if (not lDate) then lDate = struct.new('DateTime') end + elseif Type == 'date' then + -- The date type can be expressed as 'YYYY-MM-DD HH:NN', 'YYYY-MM-DD', 'YYYY-MM-DDTHH:NN'. + if not lDate then lDate = struct.new('DateTime') end local day, time = string.match(Value, '^(%d%d%d%d%-%d%d?%-%d%d?)[%sT]?(.*)$') - lDate.year, lDate.month, lDate.day = string.match(day, '^(%d+)-(%d+)-(%d+)$') local h, min, sec = string.match(time, '^(%d%d):(%d%d):?(%d?%d?)$') - if (h != nil) then + if h != nil then lDate.hour = h lDate.minute = min lDate.second = sec end return string.format('%4d-%02d-%02d %02d:%02d', lDate.year, lDate.month, lDate.day, lDate.hour, lDate.minute) - elseif (Type == 'numeric') then + elseif Type == 'numeric' then return string.format('%d', nz(tonumber(Value), 0)) - elseif (Type == 'seconds') then + elseif Type == 'seconds' then Value = nz(tonumber(Value), 0) local min = Value / 60 local sec = Value % 60 return string.format('%d:%.2d', min, sec) - elseif (Type == 'float') then + elseif Type == 'float' then return string.format('%.3f', nz(tonumber(Value), 0)) end @@ -100,7 +90,7 @@ local function setColumnGradient(View, Column) local selName = 'colSelect' .. lCounter lCounter = lCounter + 1 - if (nz(Column.align, 'left') == 'right') then + if nz(Column.align, 'left') == 'right' then Column.vecGradient = gui.simpleGradient(View.scene, defName, { { r=0,g=0,b=0,a=0,offset=0.1 }, { r=0,g=0,b=0,a=255,offset=0.2 }, { r=0,g=0,b=0,a=255,offset=1.0 } }, 0, 0, Column.width, 0, 'userspace') @@ -153,19 +143,19 @@ local function highlightItem(View, Item, Highlighted) for k, col in pairs(Item._col) do if col.text then col.text.fill = View.namedCol[k].fillItemSelect end end - Item._vectors.highlight.fill = lColours.fillBkgdSelectHighlight + Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd else for k, col in pairs(Item._col) do if col.text then col.text.fill = View.namedCol[k].fillItem end end - Item._vectors.highlight.fill = lColours.fillBkgdHighlight + Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd end - elseif (Item._selected) then + elseif Item._selected then for k, col in pairs(Item._col) do if col.text then col.text.fill = View.namedCol[k].fillItemSelect end end - Item._vectors.highlight.fill = lColours.fillSelect + Item._vectors.highlight.fill = gui.style.page.textSelectBkgd else if Item.fill then for k, col in pairs(Item._col) do @@ -195,7 +185,7 @@ local function arrangeColumns(View) local pc = struct.new('PathCommand') for _, col in pairs(View.columns) do - if (col.align == 'right') then + if col.align == 'right' then col.vecTitle.x = col.width - MARGIN else col.vecTitle.x = MARGIN @@ -219,7 +209,7 @@ local function arrangeColumns(View) if View._sort then local col = View._sort.col - if (col.align == 'right') then + if col.align == 'right' then View._sort.icon.x = MARGIN else View._sort.icon.x = col.width - MARGIN - ICON_SIZE @@ -228,7 +218,7 @@ local function arrangeColumns(View) end local function bannerZoneClick(View, Col, X, Y, Value) - if (Value == 1) then return end + if Value == 1 then return end if View._sort and View._sort.icon then View._sort.icon.free() @@ -260,14 +250,14 @@ local function bannerZoneClick(View, Col, X, Y, Value) View._sort.icon = Col.bannerVP.new('VectorRectangle', { fill = 'url(#sort_asc)', - y = math.round( ((BANNER_HEIGHT - ICON_SIZE) * 0.5)), + y = math.round((BANNER_HEIGHT - ICON_SIZE) * 0.5), width = ICON_SIZE, height = ICON_SIZE }) end end - if (Col.align == 'right') then + if Col.align == 'right' then View._sort.icon.x = MARGIN else View._sort.icon.x = Col.width - MARGIN - ICON_SIZE @@ -320,7 +310,7 @@ gui.columnView = function(Options) end View.bannerHeight = BANNER_HEIGHT - View.y = View.bannerHeight + View.y = View.bannerHeight View.banner = { } if not View.icons then View.icons = { } @@ -338,25 +328,19 @@ gui.columnView = function(Options) }) View.banner.bkgd = View.banner.viewport.new('VectorRectangle', { - x = 0, - y = 0, - width = '100%', - height = '100%', - fill = lColours.buttonBackground, - stroke = lColours.strokeButton, - strokeWidth = 2 + x = 0, y = 0, width = '100%', height = '100%', + fill = gui.style.button.bkgd, stroke = lColours.strokeButton, strokeWidth = 1 }) View.banner.lines = View.banner.viewport.new('VectorPath', { - stroke = lColours.strokeButton, - strokeWidth = 1 + stroke = lColours.strokeButton, strokeWidth = 1 }) View.banner.viewport.mtSubscribeInput(bit.bor(JTYPE_MOVEMENT, JTYPE_BUTTON), function(Viewport, Events) local ev = Events - while (ev) do - if (ev.type == JET_BUTTON_1) then - if (bit.band(ev.flags, JTYPE_REPEATED) != 0) then + while ev do + if ev.type == JET_BUTTON_1 then + if bit.band(ev.flags, JTYPE_REPEATED) != 0 then return end @@ -415,7 +399,7 @@ gui.columnView = function(Options) -- Rearrange layout local delta_x = X - SizeVP.x col.width = col.width + delta_x - if (col.width < MARGIN) then col.width = MARGIN end + if col.width < MARGIN then col.width = MARGIN end col.vecViewport.width = col.width local scan = col.next @@ -435,7 +419,7 @@ gui.columnView = function(Options) View.scrollbar.setDynamicPageSize(page_width, page_width, Page.height) end - if (col.align == 'right') then -- Realign all text in this column + if col.align == 'right' then -- Realign all text in this column local align_x = col.width - MARGIN for _, item in pairs(View.items) do if item._col and item._col[col.attrib].text then @@ -457,9 +441,9 @@ gui.columnView = function(Options) col.vecTitle.string = nz(col['title'], 'Untitled') if View._sort and (col == View._sort.col) then - col.vecTitle.fill = lcolours.fillTitleSortFont + col.vecTitle.fill = gui.style.widget.text else - col.vecTitle.fill = lColours.fillTitleFont + col.vecTitle.fill = gui.style.widget.text end x = x + col.width @@ -528,10 +512,8 @@ gui.columnView = function(Options) item._highlightEvent = highlightItem - -- Respond to positive item selection by the user (e.g. button click) - item._selectEvent = function(View, Item) - if (Item == View.hItem) then + if Item == View.hItem then Item._highlightEvent(View, Item, true) else Item._highlightEvent(View, Item, false) @@ -566,15 +548,18 @@ gui.columnView = function(Options) }) end end) - elseif (col.checkBoxes) then + elseif col.checkBoxes then -- Checkbox mode leverages the icon mechanisms that are already in place. --[[ if not glTick and not tick_error then - obj.new('picture', src="templates:images/tick") then + obj.new('picture', src='templates:images/tick') then end - if (item.checked) glTick->Bitmap->Opacity = 255 - else glTick->Bitmap->Opacity = 40 + if item.checked then + glTick->Bitmap->Opacity = 255 + else + glTick->Bitmap->Opacity = 40 + end --]] end @@ -591,7 +576,7 @@ gui.columnView = function(Options) item._col[attrib].text.y = y + LINE_OFFSET end - if (col.align == 'right') then + if col.align == 'right' then item._col[attrib].text.x = col.width - MARGIN else if col.showIcons then @@ -638,7 +623,7 @@ gui.columnView = function(Options) -- NB: Attrib values that evaluate to nil are not permitted/supported. vw.sortColumn = function(View, Column, Ascending) - if (type(Column) == 'string') then + if type(Column) == 'string' then for _, col in pairs(View.columns) do if col['attrib'] == Column then Column = col @@ -650,9 +635,9 @@ gui.columnView = function(Options) end if type(Column) == 'table' then - if (#View.items > 0) then + if #View.items > 0 then local attrib = Column['attrib'] - if (type(View.items[1][attrib]) == 'table') then + if type(View.items[1][attrib]) == 'table' then if View.items[1][attrib]['sort'] then table.sort(View.items, function(a,b) if Ascending then diff --git a/scripts/gui/filedialog.fluid b/scripts/gui/filedialog.fluid index 8fc71cbd6..a8bd2b26b 100644 --- a/scripts/gui/filedialog.fluid +++ b/scripts/gui/filedialog.fluid @@ -107,11 +107,17 @@ gui.dialog.file = function(Options) self.viewport = self.window:clientViewport({ }) + local bkgd = self.viewport.new('VectorRectangle', { + x=0, y=0, xOffset=0, height=1, fill=gui.palette.base + }) + self.toolbar = gui.toolbar({ - target = self.viewport, - x = 0, - y = 0, - xOffset = 0, + target = self.viewport, + iconTheme = 'pearl', + x = 0, + y = 0, + xOffset = 0, + flush = true, createItems = function(Bar) Bar.addGroup(1, 'Navigation', 2) @@ -163,6 +169,8 @@ gui.dialog.file = function(Options) end }) + bkgd.height = self.toolbar.viewport.height + self.pathbox = gui.input({ target = self.viewport, x = 0, diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index 6786d4367..a8542a58a 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -57,7 +57,7 @@ end local function parentWindow(Surface) local scan = obj.find(Surface.parent) while scan do - if (mSys.GetClassID(mSys.GetOwnerID(scan.id)) == ID_WINDOW) then + if mSys.GetClassID(mSys.GetOwnerID(scan.id)) == ID_WINDOW then return scan end scan = obj.find(scan.parent) @@ -96,7 +96,7 @@ end local function updateFileAttributes(Item, Info) if Item.date then - if (Item.type == 'folder') then + if Item.type == 'folder' then Item.date.sort = 'D' .. sortNum(Info.timestamp) else Item.date.sort = 'F' .. sortNum(Info.timestamp) @@ -202,7 +202,7 @@ local function applyFilters(self) end local function displayFilters(self) - if (#self.filterList < 1) then return end + if #self.filterList < 1 then return end if not self.filterViewport then -- Construct the filter list local bkgd = gui.interpolateRGB(gui.strToRGB(gui.palette.base), gui.strToRGB(gui.palette.bkgd), 0.1) @@ -263,7 +263,7 @@ local function displayFilters(self) -- Toggle the filter list - if (self.filterViewport.visibility != VIS_HIDDEN) then + if self.filterViewport.visibility != VIS_HIDDEN then self.filterViewport.visibility = VIS_HIDDEN local list_y = self._reserved_height self.view.window.y = list_y @@ -373,8 +373,8 @@ local function createFileItem(self, Info) local device = obj.new('storagedevice', { volume = Info.name .. ':' } ) if bit.band(device.deviceFlags, bit.bor(DEVICE_FLOPPY_DISK, DEVICE_FIXED, DEVICE_HARD_DISK, DEVICE_COMPACT_DISC)) != 0 then colour = 'green' - if (device.deviceSize >= 1) then total_size = sortNum(device.deviceSize) end - if (device.bytesFree >= 1) then free_space = sortNum(device.bytesFree) end + if device.deviceSize >= 1 then total_size = sortNum(device.deviceSize) end + if device.bytesFree >= 1 then free_space = sortNum(device.bytesFree) end end local display_name = Info.name ..':' @@ -420,7 +420,7 @@ local function createFileItem(self, Info) size = { sort = sortNum(0), value = nil } -- Dummy required for sorting } - if (Info.modified.year != 0) then + if Info.modified.year != 0 then new_item.date = { sort = 'D' .. sortNum(Info.timestamp), value = printableTime(Info.modified) } end elseif bit.band(Info.Flags, RDF_FILE) != 0 then @@ -579,7 +579,7 @@ gui.fileview = function(View, Options) if bit.band(info.Flags, RDF_VOLUME) != 0 and bit.band(info.Flags, RDF_HIDDEN) != 0 then -- Ignore hidden volumes - elseif (#self.view.items == 0) then -- Empty view, this is the first file. + elseif #self.view.items == 0 then -- Empty view, this is the first file. addFileItem(self, info) break else @@ -603,13 +603,13 @@ gui.fileview = function(View, Options) end end - if (#self.view.items > 0) then -- Check for deleted files + if #self.view.items > 0 then -- Check for deleted files local path = nz(self.path,'') if not self.path then -- Do not resolve the path when at the root view else -- Use ResolvePath() to prevent problems with multi-folder volumes local err, resolved_path = mSys.ResolvePath(path, RSF_NO_FILE_CHECK) - if (err == ERR_Okay) then + if err == ERR_Okay then path = resolved_path end end @@ -664,7 +664,7 @@ gui.fileview = function(View, Options) local function pathWatch(MonitoredFile, Path, Custom, Flags) if self.inRefresh then return end - if (Flags == 0) then -- No flags means a change has occurred but the host is unable to tell us what happened. + if Flags == 0 then -- No flags means a change has occurred but the host is unable to tell us what happened. self.refresh() return end @@ -673,10 +673,10 @@ gui.fileview = function(View, Options) if (bit.band(Flags, MFF_DELETE) != 0 and bit.band(Flags, MFF_SELF) != 0) or bit.band(Flags, MFF_UNMOUNT) != 0 then - if (self.path:sub(1,2) == 'cd') then - elseif (self.path:sub(1,4) == 'disk') then + if self.path:sub(1,2) == 'cd' then + elseif self.path:sub(1,4) == 'disk' then else -- Check that the folder is definitely gone and that this is not a mis-report. - if (mSys.AnalysePath(self.path, 0) != ERR_Okay) then + if mSys.AnalysePath(self.path, 0) != ERR_Okay then self.browse(nil) end end @@ -687,7 +687,7 @@ gui.fileview = function(View, Options) local info, info_err if bit.band(Flags, MFF_DELETE) == 0 then info_err, info = mSys.GetFileInfo(self.path .. nz(Path,'')) - if (info_err == ERR_Okay) then + if info_err == ERR_Okay then if lNoFiles and bit.band(info.Flags, RDF_FILE) != 0 then return end -- Use the true file name with the trailing slash if it is a folder (important -- for symbolically linked directories). @@ -698,14 +698,14 @@ gui.fileview = function(View, Options) end if bit.band(Flags, MFF_CREATE) != 0 then - if (info_err == ERR_Okay) then + if info_err == ERR_Okay then addFileItem(self, info) self.view.sort() end elseif bit.band(Flags, MFF_DELETE) != 0 then deleteItem(Path) elseif bit.band(Flags, MFF_MOVED) != 0 then -- Determine if the file has been moved in or moved out - if (info_err == ERR_Okay) then + if info_err == ERR_Okay then -- It is possible that the moved file is replacing an already existing file in -- the view (i.e. rename operation), so do a tag-check first. @@ -716,7 +716,7 @@ gui.fileview = function(View, Options) deleteItem(Path) end elseif bit.band(Flags, bit.bor(MFF_ATTRIB, MFF_CLOSED)) != 0 and File then - if (info_err == ERR_Okay) then + if info_err == ERR_Okay then local item = findItem(Path) if item then updateFileAttributes(item, info) @@ -756,7 +756,7 @@ gui.fileview = function(View, Options) for _,item in pairs(Items) do local err, class_id, command = mSys.IdentifyFile(self.path .. item.name.value, Mode, 0) - if (err == ERR_Okay) then + if err == ERR_Okay then obj.new('task', { path = command }).acActivate() end end @@ -788,7 +788,7 @@ gui.fileview = function(View, Options) end local function responseRename(Dialog, Response, State) - if (Response.id > 0) then + if Response.id > 0 then local dir = false if string.find(Dialog.var.src, '^.+[:/\\]$') then dir = true @@ -799,11 +799,11 @@ gui.fileview = function(View, Options) if Dialog.var.src == dest then return end local err, path = mSys.ResolvePath(dest, bit.bor(RSF_NO_FILE_CHECK, RSF_CASE_SENSITIVE)) - if (err == ERR_Okay) then -- Use ResolvePath() to prevent problems with multi-folder volumes + if err == ERR_Okay then -- Use ResolvePath() to prevent problems with multi-folder volumes err = mSys.AnalysePath(path, 0) end - if (err == ERR_Okay) then -- The destination exists + if err == ERR_Okay then -- The destination exists self.dlgRenameReplace = gui.dialog.message({ title = 'Confirmation Required', message = 'A file already exists that uses this name. Are you sure that you want to overwrite it?', @@ -828,10 +828,10 @@ gui.fileview = function(View, Options) local item = findItem(Dialog.var.name) if item then local new_name = State.input - if (item.type == 'folder') then + if item.type == 'folder' then item.name = new_name .. '/' item.filename = new_name .. '/' - elseif (item.type == 'volume') then + elseif item.type == 'volume' then item.name = new_name .. ':' item.filename = new_name .. ':' else @@ -867,7 +867,7 @@ gui.fileview = function(View, Options) self.browse = function(Path) if not nz(Path) or (Path == ':') then Path = nil end - if (Path == self.path) then return self.path end + if Path == self.path then return self.path end msg('fileview.browse("' .. nz(Path, 'NIL') .. '")') @@ -1073,7 +1073,7 @@ gui.fileview = function(View, Options) popover = self.window, modal = true, feedback = function(Dialog, Response, State) - if (Response.id > 0) then + if Response.id > 0 then self.view.deselectAll() local statement = '' @@ -1197,7 +1197,7 @@ gui.fileview = function(View, Options) if item then table.insert(items, item) end local current_time = mSys.PreciseTime() - if (current_time - msg_time > 20000) then + if current_time - msg_time > 20000 then if #items > 1 then self.view.addItems(items) items = { } @@ -1326,24 +1326,24 @@ gui.fileview = function(View, Options) -- NB: Sys-keys should only be enabled if cut/copy/paste support is not implemented in the core -- application. - if (Value == KEY_C) then + if Value == KEY_C then copy_files() - elseif (Value == KEY_X) then + elseif Value == KEY_X then cut_files() - elseif (Value == KEY_V) then + elseif Value == KEY_V then paste_files() - elseif (Value == KEY_A) then + elseif Value == KEY_A then self.view.selectAll() end - elseif (Value == KEY_DELETE) then + elseif Value == KEY_DELETE then self.deleteFiles() end end) self.view.viewport.mtSubscribeFeedback(bit.bor(FM_HAS_FOCUS, FM_CHILD_HAS_FOCUS, FM_LOST_FOCUS), function(Viewport, Event) - if (Event == FM_HAS_FOCUS) then + if Event == FM_HAS_FOCUS then self.hasFocus = true - elseif (Event == FM_LOST_FOCUS) then + elseif Event == FM_LOST_FOCUS then self.hasFocus = false end end) @@ -1360,7 +1360,7 @@ gui.fileview = function(View, Options) return end - if (Datatypes[1] == DATA_FILE) then + if Datatypes[1] == DATA_FILE then local items = self.view.dragItems if #items > 0 then local files = { } @@ -1454,12 +1454,12 @@ gui.fileview = function(View, Options) if lOptions.autoMode then local tags = { Item, -1 } - if (tags[1] != -1) then - if (lOptions.autoMode == 'open') then + if tags[1] != -1 then + if lOptions.autoMode == 'open' then openFiles(tags, 'Open') - elseif (lOptions.autoMode == 'edit') then + elseif lOptions.autoMode == 'edit' then openFiles(tags, 'Edit') - elseif (lOptions.autoMode == 'view') then + elseif lOptions.autoMode == 'view' then openFiles(tags, 'View') end end @@ -1478,8 +1478,6 @@ gui.fileview = function(View, Options) self.toolbar = gui.toolbar({ iconTheme = 'pearl', - hoverFill = gui.style.button.highlight, - selectFill = gui.palette.prime, target = self.view.viewport, x = 0, y = 0, diff --git a/scripts/gui/toolbar.fluid b/scripts/gui/toolbar.fluid index 2c08d5e75..56523b56b 100644 --- a/scripts/gui/toolbar.fluid +++ b/scripts/gui/toolbar.fluid @@ -87,7 +87,7 @@ gui.toolbar = function(Options) local function getCursorItem(X, Y) local item = self._first while (item) do - if (X >= item.x) and (X < item.x + item.width) and (Y >= item.y) and (Y < item.y + item.height) then + if (X >= item.x) and (X < item.x + item.width) and (Y >= item.y) and (Y < item.y + item.height) then if item.type == TI_ICON then return item end end item = item.next From effcad5067707829b47d061de5c77f8df2868c79 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Wed, 27 Nov 2024 01:07:50 +0000 Subject: [PATCH 13/17] [Scripts] Added gui.rgbToIconTheme() --- scripts/gui.fluid | 34 ++++++++++++++++++++++++---------- scripts/gui/button.fluid | 10 +--------- scripts/gui/columnview.fluid | 4 ++-- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 234195ac5..7e759189c 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -190,6 +190,20 @@ gui.rgbValue = function(Value) return max end +---------------------------------------------------------------------------------------------------------------------- + +gui.rgbToIconTheme = function(Value) + if type(Value) == 'string' then + Value = gui.strToRGB(Value) + end + + if gui.rgbValue(Value) > 127 then + return 'pearl' + else + return 'carbon' + end +end + ---------------------------------------------------------------------------------------------------------------------- -- Convert RGB table to HSV @@ -422,17 +436,17 @@ end -- prime (primary) and second (secondary prime) are colour pairs of the same hue. -- contrast is a suitable complement to prime and should be of a strongly contrasting hue -- neutral is a default background colour for unpainted areas, usually a light grey tone - ocean = { type = 'light', bkgd = '#ECF0F1', base = '#2C3E50', prime = '#3498DB', second = '#2980B9', - contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + ocean = { type = 'light', bkgd = '#ECF0F1', base = '#2C3E50', prime = '#3498DB', second = '#2980B9', + contrast = '#E74C3C', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, orangeApples = { type = 'light', bkgd = '#ffffff', base = '#19302d', prime = '#74a108', second = '#263248', contrast = '#e89214', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', - contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - sunrise = { type = 'light', bkgd = '#f9FfFc', base = '#687878', prime = '#e2581b', second = '#bd3232', - contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', - contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, - template = { type = '', bkgd = '', base = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } + greenland = { type = 'light', bkgd = '#f9FFfB', base = '#163a00', prime = '#c55500', second = '#55c500', + contrast = '#567a00', neutral = '#e9e9e0', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + sunrise = { type = 'light', bkgd = '#f9FfFc', base = '#687878', prime = '#e2581b', second = '#bd3232', + contrast = '#eb9500', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + candyfloss = { type = 'light', bkgd = '#fffaf7', base = '#375a5e', prime = '#6ac9a6', second = '#FF8B94', + contrast = '#e07780', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' }, + template = { type = '', bkgd = '', base = '', prime = '', second = '', contrast = '', neutral = 'rgb(220,220,220)', text='rgb(0,0,0)', altText='rgb(255,255,255)' } } gui.palette = gui.palettes.ocean @@ -457,7 +471,7 @@ end text = gui.palette.altText, bkgd = gui.palette.prime, stroke = 'rgb(255,255,255,60)', - strokeFocus = gui.palette.contrast, + strokeFocus = gui.palette.contrast, strokeWidth = 2, width = 160, -- Default width for common widgets (px) gap = 6, -- Recommended gap between widgets (px) diff --git a/scripts/gui/button.fluid b/scripts/gui/button.fluid index 9b2554db7..0ab4405e9 100644 --- a/scripts/gui/button.fluid +++ b/scripts/gui/button.fluid @@ -10,14 +10,6 @@ local HEIGHT = math.floor(FONT_MAX_HEIGHT * 1.75) + 1 local ICON_SIZE = HEIGHT * 0.56 local GAP = HEIGHT * 0.35 -local function iconTheme() - if (gui.rgbValue(gui.strToRGB(gui.style.widget.text)) > 127) then - return 'pearl' - else - return 'default' -- The default theme is dark - end -end - gui.button = function(Options) local self = { -- Public variables events = nz(Options.events, { }), @@ -98,7 +90,7 @@ gui.button = function(Options) if Options.icon then pcall(function() - local icon = gui.createIcon(self.viewport.scene, Options.icon, ICON_SIZE, iconTheme()) + local icon = gui.createIcon(self.viewport.scene, Options.icon, ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text)) if icon then self.iconRender = self.viewport.new('VectorViewport', { diff --git a/scripts/gui/columnview.fluid b/scripts/gui/columnview.fluid index f37befc97..c0401e6ec 100644 --- a/scripts/gui/columnview.fluid +++ b/scripts/gui/columnview.fluid @@ -229,7 +229,7 @@ local function bannerZoneClick(View, Col, X, Y, Value) View._sort = { col=Col, ascending=false } if not View.iconDescending then if not View.icons['sort_desc'] then - gui.createIcon(View.scene, 'layout/sort_alpha_desc', ICON_SIZE, nil, 'sort_desc') + gui.createIcon(View.scene, 'layout/sort_alpha_desc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_desc') View.icons['sort_desc'] = true end @@ -245,7 +245,7 @@ local function bannerZoneClick(View, Col, X, Y, Value) if not View.iconAscending then if not View.icons['sort_asc'] then View.icons['sort_asc'] = true - gui.createIcon(View.scene, 'layout/sort_alpha_asc', ICON_SIZE, nil, 'sort_asc') + gui.createIcon(View.scene, 'layout/sort_alpha_asc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_asc') end View._sort.icon = Col.bannerVP.new('VectorRectangle', { From 93692554ffbf533e222ff3f999012c370934fd22 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 28 Nov 2024 13:33:47 +0000 Subject: [PATCH 14/17] [Scripts] Improved ColumnView presentation --- scripts/gui/columnview.fluid | 48 ++++++++++++++++++++++-------------- scripts/gui/fileview.fluid | 4 +-- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/scripts/gui/columnview.fluid b/scripts/gui/columnview.fluid index c0401e6ec..c8c6fca61 100644 --- a/scripts/gui/columnview.fluid +++ b/scripts/gui/columnview.fluid @@ -143,19 +143,28 @@ local function highlightItem(View, Item, Highlighted) for k, col in pairs(Item._col) do if col.text then col.text.fill = View.namedCol[k].fillItemSelect end end - Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd - else + local col = gui.interpolateRGB(gui.strToRGB(gui.style.page.textHighlightBkgd), gui.strToRGB(gui.style.page.textSelectBkgd)) + Item._vectors.highlight.fill = gui.rgbToSVG(col) --gui.style.page.textHighlightBkgd + if Item._vectors.iconImage and Item._iconHighlight then + Item._vectors.iconImage.fill = Item._iconHighlight + end + else -- Highlighted for k, col in pairs(Item._col) do - if col.text then col.text.fill = View.namedCol[k].fillItem end + if col.text then col.text.fill = View.namedCol[k].fillItemSelect end end Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd + if Item._vectors.iconImage and Item._iconHighlight then + Item._vectors.iconImage.fill = Item._iconHighlight + end end elseif Item._selected then for k, col in pairs(Item._col) do if col.text then col.text.fill = View.namedCol[k].fillItemSelect end end - Item._vectors.highlight.fill = gui.style.page.textSelectBkgd + if Item._vectors.iconImage and Item._iconHighlight then + Item._vectors.iconImage.fill = Item._iconHighlight + end else if Item.fill then for k, col in pairs(Item._col) do @@ -173,6 +182,10 @@ local function highlightItem(View, Item, Highlighted) end end + if Item._vectors.iconImage then + Item._vectors.iconImage.fill = Item._iconFill + end + if Item._vectors.highlight then Item._vectors.highlight.free() Item._vectors.highlight = nil @@ -312,9 +325,6 @@ gui.columnView = function(Options) View.bannerHeight = BANNER_HEIGHT View.y = View.bannerHeight View.banner = { } - if not View.icons then - View.icons = { } - end -- Background graphics @@ -528,20 +538,22 @@ gui.columnView = function(Options) if col.showIcons and item.icon then pcall(function() - local uid = item.icon - if item.iconColour then - uid = uid .. '+' .. item.iconColour - end - - if not View.icons[uid] then - gui.createIcon(View.scene, item.icon, ICON_SIZE, item.iconColour, uid) - View.icons[uid] = true + local theme = nz(item.iconTheme, gui.style.page.icon) + local uid = theme .. '+' .. item.icon + local info = gui.createIcon(View.scene, item.icon, ICON_SIZE, theme, uid) + item._iconFill = info.fill + + if gui.style.page.iconHighlight and gui.style.page.iconHighlight != theme then + local theme = gui.style.page.iconHighlight + local uid = theme .. '+' .. item.icon + local info = gui.createIcon(View.scene, item.icon, ICON_SIZE, theme, uid) + item._iconHighlight = info.fill end if not item._vectors.iconImage then item._vectors.iconImage = col.vecViewport.new('VectorRectangle', { - fill = 'url(#' .. uid .. ')', - x = MARGIN, + fill = item._iconFill, + x = MARGIN * 2, y = y + ICON_OFFSET, width = ICON_SIZE, height = ICON_SIZE @@ -580,7 +592,7 @@ gui.columnView = function(Options) item._col[attrib].text.x = col.width - MARGIN else if col.showIcons then - item._col[attrib].text.x = MARGIN + ICON_SIZE + MARGIN + item._col[attrib].text.x = (MARGIN * 2) + ICON_SIZE + MARGIN else item._col[attrib].text.x = MARGIN end diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index a8542a58a..0d2b9b7ea 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -386,7 +386,7 @@ local function createFileItem(self, Info) new_item = { type = 'volume', icon = icon, - iconColour = colour, + iconTheme = colour, sort = '\x02' .. Info.name:lower(), filename = Info.name, -- Unique insensitive = true, @@ -409,7 +409,7 @@ local function createFileItem(self, Info) new_item = { type = 'folder', icon = getFileIcon(self.path .. Info.name), - iconColour = 'yellow', + iconTheme = 'yellow', sort = '\x03' .. Info.name:lower(), filename = Info.name, name = { sort = 'D' .. display_name:lower(), value = display_name }, From ea5514965ebc25bb5621179265b6db00e4dd863e Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 28 Nov 2024 21:01:16 +0000 Subject: [PATCH 15/17] [Core] Fixed free-space detection for Win32 drives; Added BOOKMARK flag to the DEVICE group. --- docs/xml/modules/classes/storagedevice.xml | 1 + docs/xml/modules/core.xml | 9 +++++---- include/parasol/modules/core.h | 1 + scripts/dev/idl/common.fdl | 3 ++- scripts/gui/fileview.fluid | 4 +++- src/core/defs.h | 2 +- src/core/fs_identify.cpp | 9 ++++----- src/core/idl.h | 2 +- src/core/lib_filesystem.cpp | 10 +++++----- src/core/microsoft/windows.cpp | 6 +++--- 10 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/xml/modules/classes/storagedevice.xml b/docs/xml/modules/classes/storagedevice.xml index 84afd6a81..89ab99fea 100644 --- a/docs/xml/modules/classes/storagedevice.xml +++ b/docs/xml/modules/classes/storagedevice.xml @@ -83,6 +83,7 @@ + This marker indicates that the presented volume is a bookmark and not a device name. Compact disc style device Catch-all for fixed media devices that are not directly identifiable. Floppy disk style device diff --git a/docs/xml/modules/core.xml b/docs/xml/modules/core.xml index 3a4a13508..666e47f73 100644 --- a/docs/xml/modules/core.xml +++ b/docs/xml/modules/core.xml @@ -928,9 +928,9 @@ GetField(Object, FID_Y|TLONG, &y); Optional argument that can refer to a variable that will store the resulting sub-class ID (if the result is a base-class, this variable will receive a value of zero). -

This function examines the relationship between file data and installed classes. It allows for instance, a JPEG file to be identified as a datatype of the Picture class, or an MP3 file to be identified as a datatype of the Sound class.

-

The method involves analysing the Path's file extension and comparing it to the supported extensions of all available classes. If a class supports the file extension then the ID of that class will be returned. If the file extension is not listed in the class dictionary or if it is listed more than once, the first 80 bytes of the file's data will be loaded and checked against classes that can match against file header information. If a match is found, the ID of the matching class will be returned.

-

This function returns an error code of ERR::Search in the event that a suitable class is not available to match against the given file.

+

This function examines the relationship between file data and Parasol classes. For instance, a JPEG file would be identified as a datatype of the Picture class. An MP3 file would be identified as a datatype of the Sound class.

+

The method involves analysing the Path's file extension and comparing it to the supported extensions of all available classes. If a class supports the file extension, the ID of that class will be returned. If the file extension is not listed in the class dictionary or if it is listed more than once, the first 80 bytes of the file's data will be loaded and checked against classes that can match against file header information. If a match is found, the ID of the matching class will be returned.

+

The ERR::Search code is returned if a suitable class does not match the targeted file.

Operation successful. @@ -1291,7 +1291,7 @@ parasol:pictures/ parasol:documents parasol:documents (Existing documents The discovered string value is returned here if found. -

Call ReadInfoTag() to retrieve the string value associated with a named tag in a FileInfo structure. The tag must have been added with AddInfoTag or ERR::NotFound will be returned.

+

ReadInfoTag() will read the value of a named tag in a FileInfo structure. The tag must have been added with AddInfoTag or ERR::NotFound will be returned.

Operation successful. @@ -2121,6 +2121,7 @@ SetField(Object, FID_Statement|TSTR, "string");
+ This marker indicates that the presented volume is a bookmark and not a device name. Compact disc style device Catch-all for fixed media devices that are not directly identifiable. Floppy disk style device diff --git a/include/parasol/modules/core.h b/include/parasol/modules/core.h index 31c68124a..f10c669d2 100644 --- a/include/parasol/modules/core.h +++ b/include/parasol/modules/core.h @@ -98,6 +98,7 @@ enum class DEVICE : LARGE { FIXED = 0x00008000, PRINTER_3D = 0x00010000, SCANNER_3D = 0x00020000, + BOOKMARK = 0x00040000, }; DEFINE_ENUM_FLAG_OPERATORS(DEVICE) diff --git a/scripts/dev/idl/common.fdl b/scripts/dev/idl/common.fdl index 98df9023d..7191acadd 100644 --- a/scripts/dev/idl/common.fdl +++ b/scripts/dev/idl/common.fdl @@ -29,7 +29,8 @@ flags("DEVICE", { bits=64, module="Core" }, "USB: Removable USB storage device. May be further qualified by `HARD_DISK`, `FLOPPY_DISK` etc", "FIXED: Catch-all for fixed media devices that are not directly identifiable.", "PRINTER_3D: Device is a three dimensional printer.", - "SCANNER_3D: Device is a three dimensional scanner.") + "SCANNER_3D: Device is a three dimensional scanner.", + "BOOKMARK: This marker indicates that the presented volume is a bookmark and not a device name.") flags("CCF", { comment="Class categories", module="Core" }, "COMMAND: Command classes perform specific procedures, like copying or moving a file, managing volumes or executing a program.", diff --git a/scripts/gui/fileview.fluid b/scripts/gui/fileview.fluid index 0d2b9b7ea..d7e2a5480 100644 --- a/scripts/gui/fileview.fluid +++ b/scripts/gui/fileview.fluid @@ -371,7 +371,9 @@ local function createFileItem(self, Info) local total_size = nil local free_space = nil local device = obj.new('storagedevice', { volume = Info.name .. ':' } ) - if bit.band(device.deviceFlags, bit.bor(DEVICE_FLOPPY_DISK, DEVICE_FIXED, DEVICE_HARD_DISK, DEVICE_COMPACT_DISC)) != 0 then + if bit.band(device.deviceFlags, DEVICE_BOOKMARK) != 0 then -- User shortcut + colour = 'carbon' + elseif bit.band(device.deviceFlags, bit.bor(DEVICE_FLOPPY_DISK, DEVICE_FIXED, DEVICE_HARD_DISK, DEVICE_COMPACT_DISC)) != 0 then colour = 'green' if device.deviceSize >= 1 then total_size = sortNum(device.deviceSize) end if device.bytesFree >= 1 then free_space = sortNum(device.bytesFree) end diff --git a/src/core/defs.h b/src/core/defs.h index 38336ba39..fb526682c 100644 --- a/src/core/defs.h +++ b/src/core/defs.h @@ -1120,7 +1120,7 @@ extern "C" void winFindClose(WINHANDLE); extern "C" void winFindNextChangeNotification(WINHANDLE); extern "C" void winGetAttrib(CSTRING, LONG *); extern "C" BYTE winGetCommand(char *, char *, LONG); -extern "C" LONG winGetFreeDiskSpace(UBYTE, LARGE *, LARGE *); +extern "C" LONG winGetFreeDiskSpace(char, LARGE *, LARGE *); extern "C" LONG winGetLogicalDrives(void); extern "C" LONG winGetLogicalDriveStrings(STRING, LONG); extern ERR winGetVolumeInformation(STRING Volume, std::string &Label, std::string &FileSystem, int &Type); diff --git a/src/core/fs_identify.cpp b/src/core/fs_identify.cpp index 81b1ebdde..7cdbd5948 100644 --- a/src/core/fs_identify.cpp +++ b/src/core/fs_identify.cpp @@ -4,18 +4,17 @@ -FUNCTION- IdentifyFile: Analyse a file and identify a class that can process it. -This function examines the relationship between file data and installed classes. It allows for instance, a JPEG file -to be identified as a datatype of the @Picture class, or an MP3 file to be identified as a datatype of the @Sound +This function examines the relationship between file data and Parasol classes. For instance, a JPEG file would be +identified as a datatype of the @Picture class. An MP3 file would be identified as a datatype of the @Sound class. The method involves analysing the `Path`'s file extension and comparing it to the supported extensions of all available -classes. If a class supports the file extension then the ID of that class will be returned. If the file extension is +classes. If a class supports the file extension, the ID of that class will be returned. If the file extension is not listed in the class dictionary or if it is listed more than once, the first 80 bytes of the file's data will be loaded and checked against classes that can match against file header information. If a match is found, the ID of the matching class will be returned. -This function returns an error code of `ERR::Search` in the event that a suitable class is not available to match -against the given file. +The `ERR::Search` code is returned if a suitable class does not match the targeted file. -INPUT- cstr Path: The location of the object data. diff --git a/src/core/idl.h b/src/core/idl.h index be17c5170..4648f3012 100644 --- a/src/core/idl.h +++ b/src/core/idl.h @@ -1 +1 @@ -char glIDL[] = { 115,46,73,110,112,117,116,69,118,101,110,116,58,112,78,101,120,116,58,73,110,112,117,116,69,118,101,110,116,44,100,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,82,101,99,105,112,105,101,110,116,73,68,44,108,79,118,101,114,73,68,44,100,65,98,115,88,44,100,65,98,115,89,44,100,88,44,100,89,44,108,68,101,118,105,99,101,73,68,44,108,84,121,112,101,44,108,70,108,97,103,115,44,108,77,97,115,107,10,115,46,100,99,82,101,113,117,101,115,116,58,108,73,116,101,109,44,99,80,114,101,102,101,114,101,110,99,101,91,52,93,10,115,46,100,99,65,117,100,105,111,58,108,83,105,122,101,44,108,70,111,114,109,97,116,10,115,46,100,99,75,101,121,69,110,116,114,121,58,108,70,108,97,103,115,44,108,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,85,110,105,99,111,100,101,10,115,46,100,99,68,101,118,105,99,101,73,110,112,117,116,58,100,86,97,108,117,101,115,91,50,93,44,120,84,105,109,101,115,116,97,109,112,44,108,68,101,118,105,99,101,73,68,44,108,70,108,97,103,115,44,108,84,121,112,101,10,115,46,68,97,116,101,84,105,109,101,58,119,89,101,97,114,44,99,77,111,110,116,104,44,99,68,97,121,44,99,72,111,117,114,44,99,77,105,110,117,116,101,44,99,83,101,99,111,110,100,44,99,84,105,109,101,90,111,110,101,10,115,46,72,83,86,58,100,72,117,101,44,100,83,97,116,117,114,97,116,105,111,110,44,100,86,97,108,117,101,44,100,65,108,112,104,97,10,115,46,70,82,71,66,58,102,82,101,100,44,102,71,114,101,101,110,44,102,66,108,117,101,44,102,65,108,112,104,97,10,115,46,82,71,66,56,58,117,99,82,101,100,44,117,99,71,114,101,101,110,44,117,99,66,108,117,101,44,117,99,65,108,112,104,97,10,115,46,82,71,66,49,54,58,117,119,82,101,100,44,117,119,71,114,101,101,110,44,117,119,66,108,117,101,44,117,119,65,108,112,104,97,10,115,46,82,71,66,51,50,58,117,108,82,101,100,44,117,108,71,114,101,101,110,44,117,108,66,108,117,101,44,117,108,65,108,112,104,97,10,115,46,82,71,66,80,97,108,101,116,116,101,58,108,65,109,116,67,111,108,111,117,114,115,44,101,67,111,108,58,82,71,66,56,91,50,53,54,93,10,115,46,67,111,108,111,117,114,70,111,114,109,97,116,58,117,99,82,101,100,83,104,105,102,116,44,117,99,71,114,101,101,110,83,104,105,102,116,44,117,99,66,108,117,101,83,104,105,102,116,44,117,99,65,108,112,104,97,83,104,105,102,116,44,117,99,82,101,100,77,97,115,107,44,117,99,71,114,101,101,110,77,97,115,107,44,117,99,66,108,117,101,77,97,115,107,44,117,99,65,108,112,104,97,77,97,115,107,44,117,99,82,101,100,80,111,115,44,117,99,71,114,101,101,110,80,111,115,44,117,99,66,108,117,101,80,111,115,44,117,99,65,108,112,104,97,80,111,115,44,117,99,66,105,116,115,80,101,114,80,105,120,101,108,10,115,46,67,108,105,112,82,101,99,116,97,110,103,108,101,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,69,100,103,101,115,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,79,98,106,101,99,116,83,105,103,110,97,108,58,111,79,98,106,101,99,116,10,115,46,70,117,110,99,116,105,111,110,70,105,101,108,100,58,115,78,97,109,101,44,117,108,84,121,112,101,10,115,46,70,117,110,99,116,105,111,110,58,112,65,100,100,114,101,115,115,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,70,105,101,108,100,65,114,114,97,121,58,115,78,97,109,101,44,112,71,101,116,70,105,101,108,100,44,112,83,101,116,70,105,101,108,100,44,109,65,114,103,44,117,108,70,108,97,103,115,10,115,46,70,105,101,108,100,68,101,102,58,115,78,97,109,101,44,108,86,97,108,117,101,10,115,46,83,121,115,116,101,109,83,116,97,116,101,58,115,80,108,97,116,102,111,114,109,44,108,67,111,110,115,111,108,101,70,68,44,108,83,116,97,103,101,10,115,46,85,110,105,116,58,100,86,97,108,117,101,44,117,108,84,121,112,101,10,115,46,65,99,116,105,111,110,65,114,114,97,121,58,112,82,111,117,116,105,110,101,44,108,65,99,116,105,111,110,67,111,100,101,10,115,46,65,99,116,105,111,110,84,97,98,108,101,58,117,108,72,97,115,104,44,108,83,105,122,101,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,67,104,105,108,100,69,110,116,114,121,58,108,79,98,106,101,99,116,73,68,44,117,108,67,108,97,115,115,73,68,10,115,46,77,101,115,115,97,103,101,58,120,84,105,109,101,44,108,85,73,68,44,108,84,121,112,101,44,108,83,105,122,101,10,115,46,77,101,109,73,110,102,111,58,112,83,116,97,114,116,44,108,79,98,106,101,99,116,73,68,44,117,108,83,105,122,101,44,108,70,108,97,103,115,44,108,77,101,109,111,114,121,73,68,44,119,65,99,99,101,115,115,67,111,117,110,116,10,115,46,67,111,109,112,114,101,115,115,105,111,110,70,101,101,100,98,97,99,107,58,108,70,101,101,100,98,97,99,107,73,68,44,108,73,110,100,101,120,44,115,80,97,116,104,44,115,68,101,115,116,44,120,80,114,111,103,114,101,115,115,44,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,119,89,101,97,114,44,119,77,111,110,116,104,44,119,68,97,121,44,119,72,111,117,114,44,119,77,105,110,117,116,101,44,119,83,101,99,111,110,100,10,115,46,67,111,109,112,114,101,115,115,101,100,73,116,101,109,58,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,112,78,101,120,116,58,67,111,109,112,114,101,115,115,101,100,73,116,101,109,44,115,80,97,116,104,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,108,79,116,104,101,114,115,73,68,44,108,70,108,97,103,115,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,70,105,108,101,73,110,102,111,58,120,83,105,122,101,44,120,84,105,109,101,83,116,97,109,112,44,112,78,101,120,116,58,70,105,108,101,73,110,102,111,44,115,78,97,109,101,44,108,70,108,97,103,115,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,68,105,114,73,110,102,111,58,112,73,110,102,111,58,70,105,108,101,73,110,102,111,10,115,46,70,105,108,101,70,101,101,100,98,97,99,107,58,120,83,105,122,101,44,120,80,111,115,105,116,105,111,110,44,115,80,97,116,104,44,115,68,101,115,116,44,108,70,101,101,100,98,97,99,107,73,68,44,99,82,101,115,101,114,118,101,100,91,51,50,93,10,115,46,70,105,101,108,100,58,109,65,114,103,44,112,71,101,116,86,97,108,117,101,44,112,83,101,116,86,97,108,117,101,44,112,87,114,105,116,101,86,97,108,117,101,44,115,78,97,109,101,44,117,108,70,105,101,108,100,73,68,44,117,119,79,102,102,115,101,116,44,117,119,73,110,100,101,120,44,117,108,70,108,97,103,115,10,99,46,65,67,58,65,99,116,105,118,97,116,101,61,48,120,50,44,67,108,101,97,114,61,48,120,52,44,67,108,105,112,98,111,97,114,100,61,48,120,50,100,44,67,111,112,121,68,97,116,97,61,48,120,55,44,68,97,116,97,70,101,101,100,61,48,120,56,44,68,101,97,99,116,105,118,97,116,101,61,48,120,57,44,68,105,115,97,98,108,101,61,48,120,50,102,44,68,114,97,103,68,114,111,112,61,48,120,49,48,44,68,114,97,119,61,48,120,97,44,69,78,68,61,48,120,51,49,44,69,110,97,98,108,101,61,48,120,54,44,70,108,117,115,104,61,48,120,98,44,70,111,99,117,115,61,48,120,99,44,70,114,101,101,61,48,120,100,44,70,114,101,101,87,97,114,110,105,110,103,61,48,120,53,44,71,101,116,75,101,121,61,48,120,102,44,72,105,100,101,61,48,120,49,49,44,73,110,105,116,61,48,120,49,50,44,76,111,99,107,61,48,120,49,51,44,76,111,115,116,70,111,99,117,115,61,48,120,49,52,44,77,111,118,101,61,48,120,49,53,44,77,111,118,101,84,111,66,97,99,107,61,48,120,49,54,44,77,111,118,101,84,111,70,114,111,110,116,61,48,120,49,55,44,77,111,118,101,84,111,80,111,105,110,116,61,48,120,50,51,44,78,101,119,67,104,105,108,100,61,48,120,49,56,44,78,101,119,79,98,106,101,99,116,61,48,120,49,97,44,78,101,119,79,119,110,101,114,61,48,120,49,57,44,78,101,119,80,108,97,99,101,109,101,110,116,61,48,120,51,48,44,78,101,120,116,61,48,120,50,57,44,80,114,101,118,61,48,120,50,97,44,81,117,101,114,121,61,48,120,49,99,44,82,101,97,100,61,48,120,49,100,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,51,44,82,101,100,111,61,48,120,49,98,44,82,101,102,114,101,115,104,61,48,120,50,101,44,82,101,110,97,109,101,61,48,120,49,101,44,82,101,115,101,116,61,48,120,49,102,44,82,101,115,105,122,101,61,48,120,50,48,44,83,97,118,101,73,109,97,103,101,61,48,120,50,49,44,83,97,118,101,83,101,116,116,105,110,103,115,61,48,120,101,44,83,97,118,101,84,111,79,98,106,101,99,116,61,48,120,50,50,44,83,101,101,107,61,48,120,50,52,44,83,101,116,70,105,101,108,100,61,48,120,50,99,44,83,101,116,75,101,121,61,48,120,50,53,44,83,104,111,119,61,48,120,50,54,44,83,105,103,110,97,108,61,48,120,49,44,85,110,100,111,61,48,120,50,55,44,85,110,108,111,99,107,61,48,120,50,56,44,87,114,105,116,101,61,48,120,50,98,10,99,46,65,76,73,71,78,58,66,79,84,84,79,77,61,48,120,50,48,44,67,69,78,84,69,82,61,48,120,99,44,72,79,82,73,90,79,78,84,65,76,61,48,120,52,44,76,69,70,84,61,48,120,49,44,77,73,68,68,76,69,61,48,120,99,44,82,73,71,72,84,61,48,120,50,44,84,79,80,61,48,120,49,48,44,86,69,82,84,73,67,65,76,61,48,120,56,10,99,46,67,67,70,58,65,85,68,73,79,61,48,120,56,48,44,67,79,77,77,65,78,68,61,48,120,49,44,68,65,84,65,61,48,120,49,48,48,44,70,73,76,69,83,89,83,84,69,77,61,48,120,50,44,71,82,65,80,72,73,67,83,61,48,120,52,44,71,85,73,61,48,120,56,44,73,79,61,48,120,49,48,44,77,73,83,67,61,48,120,50,48,48,44,77,85,76,84,73,77,69,68,73,65,61,48,120,56,48,48,44,78,69,84,87,79,82,75,61,48,120,52,48,48,44,83,89,83,84,69,77,61,48,120,50,48,44,84,79,79,76,61,48,120,52,48,10,99,46,67,70,58,68,69,70,76,65,84,69,61,48,120,51,44,71,90,73,80,61,48,120,49,44,90,76,73,66,61,48,120,50,10,99,46,67,76,70,58,73,78,72,69,82,73,84,95,76,79,67,65,76,61,48,120,49,44,78,79,95,79,87,78,69,82,83,72,73,80,61,48,120,50,10,99,46,67,76,73,80,77,79,68,69,58,67,79,80,89,61,48,120,50,44,67,85,84,61,48,120,49,44,80,65,83,84,69,61,48,120,52,10,99,46,67,77,70,58,65,80,80,76,89,95,83,69,67,85,82,73,84,89,61,48,120,50,48,44,67,82,69,65,84,69,95,70,73,76,69,61,48,120,52,44,78,69,87,61,48,120,50,44,78,79,95,76,73,78,75,83,61,48,120,49,48,44,80,65,83,83,87,79,82,68,61,48,120,49,44,82,69,65,68,95,79,78,76,89,61,48,120,56,10,99,46,67,78,70,58,65,85,84,79,95,83,65,86,69,61,48,120,50,44,78,69,87,61,48,120,56,44,79,80,84,73,79,78,65,76,95,70,73,76,69,83,61,48,120,52,44,83,84,82,73,80,95,81,85,79,84,69,83,61,48,120,49,10,99,46,68,65,84,65,58,65,85,68,73,79,61,48,120,53,44,67,79,78,84,69,78,84,61,48,120,98,44,68,69,86,73,67,69,95,73,78,80,85,84,61,48,120,51,44,70,73,76,69,61,48,120,97,44,73,77,65,71,69,61,48,120,55,44,73,78,80,85,84,95,82,69,65,68,89,61,48,120,99,44,82,65,87,61,48,120,50,44,82,69,67,69,73,80,84,61,48,120,57,44,82,69,67,79,82,68,61,48,120,54,44,82,69,81,85,69,83,84,61,48,120,56,44,84,69,88,84,61,48,120,49,44,88,77,76,61,48,120,52,10,99,46,68,69,86,73,67,69,58,67,79,77,80,65,67,84,95,68,73,83,67,61,48,120,49,44,70,73,88,69,68,61,48,120,56,48,48,48,44,70,76,79,80,80,89,95,68,73,83,75,61,48,120,52,44,72,65,82,68,95,68,73,83,75,61,48,120,50,44,77,69,77,79,82,89,61,48,120,49,48,48,48,44,77,79,68,69,77,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,44,80,82,73,78,84,69,82,61,48,120,50,48,48,44,80,82,73,78,84,69,82,95,51,68,61,48,120,49,48,48,48,48,44,82,69,65,68,61,48,120,56,44,82,69,77,79,86,65,66,76,69,61,48,120,50,48,44,82,69,77,79,86,69,65,66,76,69,61,48,120,50,48,44,83,67,65,78,78,69,82,61,48,120,52,48,48,44,83,67,65,78,78,69,82,95,51,68,61,48,120,50,48,48,48,48,44,83,79,70,84,87,65,82,69,61,48,120,52,48,44,84,65,80,69,61,48,120,49,48,48,44,84,69,77,80,79,82,65,82,89,61,48,120,56,48,48,44,85,83,66,61,48,120,52,48,48,48,44,87,82,73,84,69,61,48,120,49,48,10,99,46,68,77,70,58,70,73,88,69,68,95,67,69,78,84,69,82,95,88,61,48,120,49,48,48,48,48,48,44,70,73,88,69,68,95,67,69,78,84,69,82,95,89,61,48,120,50,48,48,48,48,48,44,70,73,88,69,68,95,68,69,80,84,72,61,48,120,49,48,48,48,44,70,73,88,69,68,95,72,69,73,71,72,84,61,48,120,49,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,88,61,48,120,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,89,61,48,120,50,48,48,48,48,48,48,44,70,73,88,69,68,95,87,73,68,84,72,61,48,120,50,48,48,44,70,73,88,69,68,95,88,61,48,120,52,44,70,73,88,69,68,95,88,95,79,70,70,83,69,84,61,48,120,52,48,44,70,73,88,69,68,95,89,61,48,120,56,44,70,73,88,69,68,95,89,95,79,70,70,83,69,84,61,48,120,56,48,44,70,73,88,69,68,95,90,61,48,120,52,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,88,61,48,120,52,48,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,89,61,48,120,56,48,48,48,48,44,83,67,65,76,69,68,95,68,69,80,84,72,61,48,120,50,48,48,48,44,83,67,65,76,69,68,95,72,69,73,71,72,84,61,48,120,52,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,88,61,48,120,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,89,61,48,120,49,48,48,48,48,48,48,44,83,67,65,76,69,68,95,87,73,68,84,72,61,48,120,56,48,48,44,83,67,65,76,69,68,95,88,61,48,120,49,44,83,67,65,76,69,68,95,88,95,79,70,70,83,69,84,61,48,120,49,48,44,83,67,65,76,69,68,95,89,61,48,120,50,44,83,67,65,76,69,68,95,89,95,79,70,70,83,69,84,61,48,120,50,48,44,83,67,65,76,69,68,95,90,61,48,120,56,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,72,61,48,120,52,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,86,61,48,120,56,48,48,48,48,48,10,99,46,68,82,76,58,68,79,87,78,61,48,120,49,44,69,65,83,84,61,48,120,50,44,76,69,70,84,61,48,120,51,44,78,79,82,84,72,61,48,120,48,44,78,79,82,84,72,95,69,65,83,84,61,48,120,52,44,78,79,82,84,72,95,87,69,83,84,61,48,120,53,44,82,73,71,72,84,61,48,120,50,44,83,79,85,84,72,61,48,120,49,44,83,79,85,84,72,95,69,65,83,84,61,48,120,54,44,83,79,85,84,72,95,87,69,83,84,61,48,120,55,44,85,80,61,48,120,48,44,87,69,83,84,61,48,120,51,10,99,46,69,68,71,69,58,65,76,76,61,48,120,102,102,44,66,79,84,84,79,77,61,48,120,56,44,66,79,84,84,79,77,95,76,69,70,84,61,48,120,52,48,44,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,82,73,71,72,84,61,48,120,52,44,84,79,80,61,48,120,49,44,84,79,80,95,76,69,70,84,61,48,120,49,48,44,84,79,80,95,82,73,71,72,84,61,48,120,50,48,10,99,46,69,82,70,58,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,10,99,46,69,82,82,58,65,99,99,101,115,115,77,101,109,111,114,121,61,48,120,52,98,44,65,99,99,101,115,115,79,98,106,101,99,116,61,48,120,53,51,44,65,99,99,101,115,115,83,101,109,97,112,104,111,114,101,61,48,120,55,51,44,65,99,116,105,118,97,116,101,61,48,120,52,50,44,65,100,100,67,108,97,115,115,61,48,120,52,49,44,65,108,108,111,99,77,101,109,111,114,121,61,48,120,53,52,44,65,108,108,111,99,83,101,109,97,112,104,111,114,101,61,48,120,55,50,44,65,108,114,101,97,100,121,68,101,102,105,110,101,100,61,48,120,98,54,44,65,108,114,101,97,100,121,76,111,99,107,101,100,61,48,120,57,99,44,65,114,103,115,61,48,120,49,52,44,65,114,114,97,121,70,117,108,108,61,48,120,50,100,44,66,117,102,102,101,114,79,118,101,114,102,108,111,119,61,48,120,53,99,44,66,117,115,121,61,48,120,56,98,44,67,97,110,99,101,108,108,101,100,61,48,120,51,44,67,97,114,100,82,101,97,100,101,114,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,102,44,67,97,114,100,82,101,97,100,101,114,85,110,107,110,111,119,110,61,48,120,57,100,44,67,111,109,112,114,101,115,115,105,111,110,61,48,120,97,100,44,67,111,110,110,101,99,116,105,111,110,65,98,111,114,116,101,100,61,48,120,56,99,44,67,111,110,110,101,99,116,105,111,110,82,101,102,117,115,101,100,61,48,120,56,51,44,67,111,110,115,116,114,97,105,110,116,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,67,111,110,116,105,110,117,101,61,48,120,53,44,67,111,114,101,86,101,114,115,105,111,110,61,48,120,50,54,44,67,114,101,97,116,101,70,105,108,101,61,48,120,55,52,44,67,114,101,97,116,101,79,98,106,101,99,116,61,48,120,54,53,44,67,114,101,97,116,101,82,101,115,111,117,114,99,101,61,48,120,98,50,44,68,97,116,97,83,105,122,101,61,48,120,56,97,44,68,101,97,99,116,105,118,97,116,101,100,61,48,120,57,97,44,68,101,97,100,76,111,99,107,61,48,120,51,101,44,68,101,99,111,109,112,114,101,115,115,105,111,110,61,48,120,97,99,44,68,101,108,101,116,101,70,105,108,101,61,48,120,55,53,44,68,105,114,69,109,112,116,121,61,48,120,56,44,68,105,115,99,111,110,110,101,99,116,101,100,61,48,120,56,54,44,68,111,78,111,116,69,120,112,117,110,103,101,61,48,120,51,48,44,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,55,56,44,68,111,117,98,108,101,73,110,105,116,61,48,120,52,51,44,68,114,97,119,61,48,120,52,56,44,69,78,68,61,48,120,98,101,44,69,109,112,116,121,83,116,114,105,110,103,61,48,120,54,100,44,69,110,100,79,102,70,105,108,101,61,48,120,55,101,44,69,110,116,114,121,77,105,115,115,105,110,103,72,101,97,100,101,114,61,48,120,51,97,44,69,120,97,109,105,110,101,70,97,105,108,101,100,61,48,120,49,57,44,69,120,99,101,112,116,105,111,110,61,48,120,97,51,44,69,120,99,101,112,116,105,111,110,84,104,114,101,115,104,111,108,100,61,48,120,57,44,69,120,99,108,117,115,105,118,101,68,101,110,105,101,100,61,48,120,53,51,44,69,120,101,99,86,105,111,108,97,116,105,111,110,61,48,120,56,102,44,69,120,105,115,116,115,61,48,120,55,97,44,69,120,112,101,99,116,101,100,70,105,108,101,61,48,120,54,102,44,69,120,112,101,99,116,101,100,70,111,108,100,101,114,61,48,120,97,101,44,70,97,105,108,101,100,61,48,120,100,44,70,97,108,115,101,61,48,120,49,44,70,105,101,108,100,78,111,116,83,101,116,61,48,120,52,52,44,70,105,101,108,100,83,101,97,114,99,104,61,48,120,51,50,44,70,105,101,108,100,84,121,112,101,77,105,115,109,97,116,99,104,61,48,120,53,97,44,70,105,108,101,61,48,120,101,44,70,105,108,101,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,49,50,44,70,105,108,101,69,120,105,115,116,115,61,48,120,54,51,44,70,105,108,101,78,111,116,70,111,117,110,100,61,48,120,49,50,44,70,105,108,101,82,101,97,100,70,108,97,103,61,48,120,52,54,44,70,105,108,101,87,114,105,116,101,70,108,97,103,61,48,120,52,55,44,70,105,110,105,115,104,101,100,61,48,120,55,101,44,70,117,110,99,116,105,111,110,61,48,120,98,53,44,71,101,116,70,105,101,108,100,61,48,120,53,54,44,71,101,116,83,117,114,102,97,99,101,73,110,102,111,61,48,120,55,100,44,72,111,115,116,78,111,116,70,111,117,110,100,61,48,120,56,49,44,72,111,115,116,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,53,44,73,100,101,110,116,105,99,97,108,80,97,116,104,115,61,48,120,55,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,65,116,116,101,109,112,116,61,48,120,51,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,73,68,61,48,120,51,55,44,73,108,108,101,103,97,108,65,100,100,114,101,115,115,61,48,120,57,49,44,73,108,108,101,103,97,108,77,101,116,104,111,100,73,68,61,48,120,51,54,44,73,109,109,117,116,97,98,108,101,61,48,120,97,102,44,73,110,85,115,101,61,48,120,99,44,73,110,105,116,61,48,120,50,49,44,73,110,105,116,77,111,100,117,108,101,61,48,120,49,49,44,73,110,112,117,116,79,117,116,112,117,116,61,48,120,57,52,44,73,110,116,101,103,114,105,116,121,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,73,110,118,97,108,105,100,68,97,116,97,61,48,120,102,44,73,110,118,97,108,105,100,68,105,109,101,110,115,105,111,110,61,48,120,53,57,44,73,110,118,97,108,105,100,72,84,84,80,82,101,115,112,111,110,115,101,61,48,120,97,49,44,73,110,118,97,108,105,100,72,97,110,100,108,101,61,48,120,57,54,44,73,110,118,97,108,105,100,79,98,106,101,99,116,61,48,120,56,101,44,73,110,118,97,108,105,100,80,97,116,104,61,48,120,51,51,44,73,110,118,97,108,105,100,82,101,102,101,114,101,110,99,101,61,48,120,97,50,44,73,110,118,97,108,105,100,83,116,97,116,101,61,48,120,56,48,44,73,110,118,97,108,105,100,85,82,73,61,48,120,56,50,44,73,110,118,97,108,105,100,86,97,108,117,101,61,48,120,57,56,44,76,105,109,105,116,101,100,83,117,99,99,101,115,115,61,48,120,50,44,76,105,115,116,67,104,105,108,100,114,101,110,61,48,120,54,97,44,76,111,97,100,77,111,100,117,108,101,61,48,120,57,53,44,76,111,99,107,61,48,120,49,56,44,76,111,99,107,70,97,105,108,101,100,61,48,120,49,56,44,76,111,99,107,77,117,116,101,120,61,48,120,97,97,44,76,111,99,107,82,101,113,117,105,114,101,100,61,48,120,57,98,44,76,111,99,107,101,100,61,48,120,57,99,44,76,111,111,112,61,48,120,54,50,44,76,111,115,116,67,108,97,115,115,61,48,120,49,97,44,76,111,115,116,79,119,110,101,114,61,48,120,50,102,44,76,111,119,67,97,112,97,99,105,116,121,61,48,120,50,48,44,77,97,114,107,101,100,70,111,114,68,101,108,101,116,105,111,110,61,48,120,51,53,44,77,101,109,111,114,121,61,48,120,49,100,44,77,101,109,111,114,121,67,111,114,114,117,112,116,61,48,120,51,49,44,77,101,109,111,114,121,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,51,100,44,77,101,109,111,114,121,73,110,102,111,61,48,120,54,54,44,77,105,115,109,97,116,99,104,61,48,120,53,101,44,77,105,115,115,105,110,103,67,108,97,115,115,61,48,120,52,53,44,77,105,115,115,105,110,103,67,108,97,115,115,78,97,109,101,61,48,120,50,97,44,77,105,115,115,105,110,103,80,97,116,104,61,48,120,52,99,44,77,111,100,117,108,101,73,110,105,116,70,97,105,108,101,100,61,48,120,51,99,44,77,111,100,117,108,101,77,105,115,115,105,110,103,73,110,105,116,61,48,120,51,98,44,77,111,100,117,108,101,77,105,115,115,105,110,103,78,97,109,101,61,48,120,52,48,44,77,111,100,117,108,101,79,112,101,110,70,97,105,108,101,100,61,48,120,51,56,44,78,101,101,100,79,119,110,101,114,61,48,120,50,52,44,78,101,101,100,87,105,100,116,104,72,101,105,103,104,116,61,48,120,50,55,44,78,101,103,97,116,105,118,101,67,108,97,115,115,73,68,61,48,120,50,57,44,78,101,103,97,116,105,118,101,83,117,98,67,108,97,115,115,73,68,61,48,120,50,56,44,78,101,116,119,111,114,107,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,52,44,78,101,119,79,98,106,101,99,116,61,48,120,53,53,44,78,111,65,99,116,105,111,110,61,48,120,49,98,44,78,111,68,97,116,97,61,48,120,49,53,44,78,111,70,105,101,108,100,65,99,99,101,115,115,61,48,120,53,55,44,78,111,77,97,116,99,104,105,110,103,79,98,106,101,99,116,61,48,120,52,97,44,78,111,77,101,100,105,97,73,110,115,101,114,116,101,100,61,48,120,57,101,44,78,111,77,101,109,111,114,121,61,48,120,97,44,78,111,77,101,116,104,111,100,115,61,48,120,52,57,44,78,111,80,101,114,109,105,115,115,105,111,110,61,48,120,50,50,44,78,111,80,111,105,110,116,101,114,61,48,120,98,44,78,111,83,101,97,114,99,104,82,101,115,117,108,116,61,48,120,52,101,44,78,111,83,116,97,116,115,61,48,120,49,102,44,78,111,83,117,112,112,111,114,116,61,48,120,49,99,44,78,111,116,70,111,117,110,100,61,48,120,49,48,44,78,111,116,73,110,105,116,105,97,108,105,115,101,100,61,48,120,54,55,44,78,111,116,76,111,99,107,101,100,61,48,120,52,100,44,78,111,116,80,111,115,115,105,98,108,101,61,48,120,98,51,44,78,111,116,104,105,110,103,68,111,110,101,61,48,120,52,44,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,44,78,117,108,108,65,114,103,115,61,48,120,56,100,44,79,98,106,101,99,116,67,111,114,114,117,112,116,61,48,120,53,48,44,79,98,106,101,99,116,69,120,105,115,116,115,61,48,120,54,101,44,79,98,115,111,108,101,116,101,61,48,120,98,49,44,79,98,116,97,105,110,77,101,116,104,111,100,61,48,120,50,99,44,79,107,97,121,61,48,120,48,44,79,112,101,110,70,105,108,101,61,48,120,55,54,44,79,112,101,110,71,76,61,48,120,97,53,44,79,117,116,79,102,66,111,117,110,100,115,61,48,120,53,102,44,79,117,116,79,102,68,97,116,97,61,48,120,55,101,44,79,117,116,79,102,82,97,110,103,101,61,48,120,50,98,44,79,117,116,79,102,83,112,97,99,101,61,48,120,55,99,44,79,117,116,115,105,100,101,77,97,105,110,84,104,114,101,97,100,61,48,120,97,54,44,79,119,110,101,114,78,101,101,100,115,66,105,116,109,97,112,61,48,120,50,53,44,79,119,110,101,114,80,97,115,115,84,104,114,111,117,103,104,61,48,120,53,49,44,80,101,114,109,105,115,115,105,111,110,68,101,110,105,101,100,61,48,120,50,50,44,80,101,114,109,105,115,115,105,111,110,115,61,48,120,50,50,44,80,114,111,120,121,83,83,76,84,117,110,110,101,108,61,48,120,97,48,44,81,117,101,114,121,61,48,120,50,101,44,82,101,97,100,61,48,120,49,54,44,82,101,97,100,70,105,108,101,84,111,66,117,102,102,101,114,61,48,120,98,48,44,82,101,97,100,79,110,108,121,61,48,120,55,55,44,82,101,97,108,108,111,99,77,101,109,111,114,121,61,48,120,54,49,44,82,101,99,117,114,115,105,111,110,61,48,120,57,48,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,55,49,44,82,101,102,114,101,115,104,61,48,120,54,57,44,82,101,115,105,122,101,61,48,120,55,48,44,82,101,115,111,108,118,101,80,97,116,104,61,48,120,54,52,44,82,101,115,111,108,118,101,83,121,109,98,111,108,61,48,120,98,52,44,82,101,115,111,117,114,99,101,69,120,105,115,116,115,61,48,120,54,56,44,82,101,116,114,121,61,48,120,55,44,83,97,110,105,116,121,70,97,105,108,117,114,101,61,48,120,55,98,44,83,99,104,101,109,97,86,105,111,108,97,116,105,111,110,61,48,120,56,57,44,83,101,97,114,99,104,61,48,120,49,48,44,83,101,99,117,114,105,116,121,61,48,120,57,55,44,83,101,101,107,61,48,120,54,48,44,83,101,114,118,105,99,101,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,57,44,83,101,116,70,105,101,108,100,61,48,120,51,52,44,83,101,116,86,97,108,117,101,78,111,116,65,114,114,97,121,61,48,120,98,99,44,83,101,116,86,97,108,117,101,78,111,116,70,117,110,99,116,105,111,110,61,48,120,98,97,44,83,101,116,86,97,108,117,101,78,111,116,76,111,111,107,117,112,61,48,120,98,100,44,83,101,116,86,97,108,117,101,78,111,116,78,117,109,101,114,105,99,61,48,120,98,55,44,83,101,116,86,97,108,117,101,78,111,116,79,98,106,101,99,116,61,48,120,98,57,44,83,101,116,86,97,108,117,101,78,111,116,80,111,105,110,116,101,114,61,48,120,98,98,44,83,101,116,86,97,108,117,101,78,111,116,83,116,114,105,110,103,61,48,120,98,56,44,83,101,116,86,111,108,117,109,101,61,48,120,97,98,44,83,107,105,112,61,48,120,54,44,83,109,97,108,108,77,97,115,107,61,48,120,54,99,44,83,116,97,116,101,109,101,110,116,85,110,115,97,116,105,115,102,105,101,100,61,48,120,52,102,44,83,116,114,105,110,103,70,111,114,109,97,116,61,48,120,55,102,44,83,121,110,116,97,120,61,48,120,55,102,44,83,121,115,116,101,109,67,97,108,108,61,48,120,54,98,44,83,121,115,116,101,109,67,111,114,114,117,112,116,61,48,120,50,51,44,83,121,115,116,101,109,76,111,99,107,101,100,61,48,120,51,102,44,84,97,115,107,83,116,105,108,108,69,120,105,115,116,115,61,48,120,56,55,44,84,101,114,109,105,110,97,116,101,61,48,120,57,44,84,104,114,101,97,100,65,108,114,101,97,100,121,65,99,116,105,118,101,61,48,120,97,52,44,84,104,114,101,97,100,78,111,116,76,111,99,107,101,100,61,48,120,97,57,44,84,105,109,101,79,117,116,61,48,120,49,101,44,84,114,117,101,61,48,120,48,44,85,110,98,97,108,97,110,99,101,100,88,77,76,61,48,120,57,50,44,85,110,100,101,102,105,110,101,100,70,105,101,108,100,61,48,120,52,52,44,85,110,114,101,99,111,103,110,105,115,101,100,70,105,101,108,100,84,121,112,101,61,48,120,53,98,44,85,110,115,117,112,112,111,114,116,101,100,70,105,101,108,100,61,48,120,53,100,44,85,110,115,117,112,112,111,114,116,101,100,79,119,110,101,114,61,48,120,53,50,44,85,115,101,83,117,98,67,108,97,115,115,61,48,120,97,55,44,86,105,114,116,117,97,108,86,111,108,117,109,101,61,48,120,53,56,44,87,111,117,108,100,66,108,111,99,107,61,48,120,57,51,44,87,114,105,116,101,61,48,120,49,55,44,87,114,111,110,103,67,108,97,115,115,61,48,120,56,101,44,87,114,111,110,103,79,98,106,101,99,116,84,121,112,101,61,48,120,56,101,44,87,114,111,110,103,84,121,112,101,61,48,120,97,56,44,87,114,111,110,103,86,101,114,115,105,111,110,61,48,120,49,51,10,99,46,69,86,71,58,65,78,68,82,79,73,68,61,48,120,100,44,65,80,80,61,48,120,99,44,65,85,68,73,79,61,48,120,56,44,67,76,65,83,83,61,48,120,98,44,68,73,83,80,76,65,89,61,48,120,53,44,69,78,68,61,48,120,101,44,70,73,76,69,83,89,83,84,69,77,61,48,120,49,44,71,85,73,61,48,120,52,44,72,65,82,68,87,65,82,69,61,48,120,55,44,73,79,61,48,120,54,44,78,69,84,87,79,82,75,61,48,120,50,44,80,79,87,69,82,61,48,120,97,44,83,89,83,84,69,77,61,48,120,51,44,85,83,69,82,61,48,120,57,10,99,46,70,66,75,58,67,79,80,89,95,70,73,76,69,61,48,120,50,44,68,69,76,69,84,69,95,70,73,76,69,61,48,120,51,44,77,79,86,69,95,70,73,76,69,61,48,120,49,10,99,46,70,68,58,65,76,76,79,67,61,48,120,50,48,44,65,82,82,65,89,61,48,120,49,48,48,48,44,65,82,82,65,89,83,73,90,69,61,48,120,56,48,44,66,85,70,70,69,82,61,48,120,50,48,48,44,66,85,70,83,73,90,69,61,48,120,56,48,44,66,89,84,69,61,48,120,49,48,48,48,48,48,48,44,67,80,80,61,48,120,52,48,48,48,44,67,85,83,84,79,77,61,48,120,56,48,48,48,44,68,79,85,66,76,69,61,48,120,56,48,48,48,48,48,48,48,44,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,48,48,48,48,49,48,48,44,69,82,82,79,82,61,48,120,56,48,48,44,70,76,65,71,83,61,48,120,52,48,44,70,76,79,65,84,61,48,120,49,48,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,61,48,120,50,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,80,84,82,61,48,120,97,48,48,48,48,48,48,44,73,61,48,120,52,48,48,44,73,78,73,84,61,48,120,52,48,48,44,76,65,82,71,69,61,48,120,52,48,48,48,48,48,48,44,76,65,82,71,69,82,69,83,85,76,84,61,48,120,52,48,48,48,49,48,48,44,76,79,67,65,76,61,48,120,50,44,76,79,78,71,61,48,120,52,48,48,48,48,48,48,48,44,76,79,78,71,82,69,83,85,76,84,61,48,120,52,48,48,48,48,49,48,48,44,76,79,79,75,85,80,61,48,120,56,48,44,79,66,74,69,67,84,61,48,120,49,44,79,66,74,69,67,84,73,68,61,48,120,52,48,48,48,48,48,48,49,44,79,66,74,69,67,84,80,84,82,61,48,120,56,48,48,48,48,48,49,44,80,79,73,78,84,69,82,61,48,120,56,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,49,48,48,48,48,44,80,84,82,61,48,120,56,48,48,48,48,48,48,44,80,84,82,66,85,70,70,69,82,61,48,120,56,48,48,48,50,48,48,44,80,84,82,82,69,83,85,76,84,61,48,120,56,48,48,48,49,48,48,44,80,84,82,83,73,90,69,61,48,120,56,48,44,80,84,82,95,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,56,48,48,48,49,48,48,44,80,84,82,95,76,65,82,71,69,82,69,83,85,76,84,61,48,120,99,48,48,48,49,48,48,44,80,84,82,95,76,79,78,71,82,69,83,85,76,84,61,48,120,52,56,48,48,48,49,48,48,44,82,61,48,120,49,48,48,44,82,69,65,68,61,48,120,49,48,48,44,82,69,83,79,85,82,67,69,61,48,120,50,48,48,48,44,82,69,83,85,76,84,61,48,120,49,48,48,44,82,71,66,61,48,120,56,48,48,48,48,44,82,73,61,48,120,53,48,48,44,82,87,61,48,120,51,48,48,44,83,67,65,76,69,68,61,48,120,50,48,48,48,48,48,44,83,84,82,61,48,120,56,48,48,48,48,48,44,83,84,82,73,78,71,61,48,120,56,48,48,48,48,48,44,83,84,82,82,69,83,85,76,84,61,48,120,56,48,48,49,48,48,44,83,84,82,85,67,84,61,48,120,49,48,44,83,89,78,79,78,89,77,61,48,120,50,48,48,48,48,44,83,89,83,84,69,77,61,48,120,49,48,48,48,48,44,84,65,71,83,61,48,120,52,48,48,44,85,78,73,84,61,48,120,50,48,48,48,48,48,48,48,44,85,78,83,73,71,78,69,68,61,48,120,52,48,48,48,48,44,86,65,82,84,65,71,83,61,48,120,52,48,44,86,73,82,84,85,65,76,61,48,120,56,44,86,79,73,68,61,48,120,48,44,86,79,76,65,84,73,76,69,61,48,120,48,44,87,61,48,120,50,48,48,44,87,79,82,68,61,48,120,52,48,48,48,48,48,44,87,82,73,84,69,61,48,120,50,48,48,10,99,46,70,68,66,58,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,50,44,68,69,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,49,44,68,69,67,79,77,80,82,69,83,83,95,79,66,74,69,67,84,61,48,120,52,44,82,69,77,79,86,69,95,70,73,76,69,61,48,120,51,10,99,46,70,68,76,58,70,69,69,68,66,65,67,75,61,48,120,49,10,99,46,70,68,84,58,65,67,67,69,83,83,69,68,61,48,120,50,44,65,82,67,72,73,86,69,68,61,48,120,51,44,67,82,69,65,84,69,68,61,48,120,49,44,77,79,68,73,70,73,69,68,61,48,120,48,10,99,46,70,70,82,58,65,66,79,82,84,61,48,120,50,44,67,79,78,84,73,78,85,69,61,48,120,48,44,79,75,65,89,61,48,120,48,44,83,75,73,80,61,48,120,49,10,99,46,70,76,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,49,48,44,66,85,70,70,69,82,61,48,120,52,48,44,68,69,86,73,67,69,61,48,120,52,48,48,44,68,73,82,69,67,84,79,82,89,61,48,120,56,44,69,88,67,76,85,68,69,95,70,73,76,69,83,61,48,120,49,48,48,48,44,69,88,67,76,85,68,69,95,70,79,76,68,69,82,83,61,48,120,50,48,48,48,44,70,73,76,69,61,48,120,49,48,48,44,70,79,76,68,69,82,61,48,120,56,44,76,73,78,75,61,48,120,50,48,44,76,79,79,80,61,48,120,56,48,44,78,69,87,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,83,69,84,95,68,65,84,69,61,48,120,50,48,48,44,83,84,82,69,65,77,61,48,120,56,48,48,44,87,82,73,84,69,61,48,120,49,10,99,46,70,79,70,58,83,77,65,82,84,95,78,65,77,69,83,61,48,120,49,10,99,46,73,68,84,89,80,69,58,70,85,78,67,84,73,79,78,61,48,120,51,44,71,76,79,66,65,76,61,48,120,50,44,77,69,83,83,65,71,69,61,48,120,49,10,99,46,74,69,84,58,65,66,83,95,88,89,61,48,120,49,98,44,65,78,65,76,79,71,50,95,88,89,61,48,120,49,54,44,65,78,65,76,79,71,50,95,90,61,48,120,49,55,44,65,78,65,76,79,71,95,88,89,61,48,120,49,52,44,65,78,65,76,79,71,95,90,61,48,120,49,53,44,66,85,84,84,79,78,95,49,61,48,120,50,44,66,85,84,84,79,78,95,49,48,61,48,120,98,44,66,85,84,84,79,78,95,50,61,48,120,51,44,66,85,84,84,79,78,95,51,61,48,120,52,44,66,85,84,84,79,78,95,52,61,48,120,53,44,66,85,84,84,79,78,95,53,61,48,120,54,44,66,85,84,84,79,78,95,54,61,48,120,55,44,66,85,84,84,79,78,95,55,61,48,120,56,44,66,85,84,84,79,78,95,56,61,48,120,57,44,66,85,84,84,79,78,95,57,61,48,120,97,44,66,85,84,84,79,78,95,83,69,76,69,67,84,61,48,120,102,44,66,85,84,84,79,78,95,83,84,65,82,84,61,48,120,101,44,67,82,79,83,83,69,68,95,73,78,61,48,120,49,99,44,67,82,79,83,83,69,68,95,79,85,84,61,48,120,49,100,44,68,69,86,73,67,69,95,84,73,76,84,95,88,89,61,48,120,49,102,44,68,69,86,73,67,69,95,84,73,76,84,95,90,61,48,120,50,48,44,68,73,71,73,84,65,76,95,88,89,61,48,120,49,44,68,73,83,80,76,65,89,95,69,68,71,69,61,48,120,50,49,44,69,78,68,61,48,120,50,50,44,76,69,70,84,95,66,85,77,80,69,82,95,49,61,48,120,49,48,44,76,69,70,84,95,66,85,77,80,69,82,95,50,61,48,120,49,49,44,76,77,66,61,48,120,50,44,77,77,66,61,48,120,52,44,80,69,78,95,84,73,76,84,95,88,89,61,48,120,49,97,44,80,82,69,83,83,85,82,69,61,48,120,49,101,44,82,73,71,72,84,95,66,85,77,80,69,82,95,49,61,48,120,49,50,44,82,73,71,72,84,95,66,85,77,80,69,82,95,50,61,48,120,49,51,44,82,77,66,61,48,120,51,44,84,82,73,71,71,69,82,95,76,69,70,84,61,48,120,99,44,84,82,73,71,71,69,82,95,82,73,71,72,84,61,48,120,100,44,87,72,69,69,76,61,48,120,49,56,44,87,72,69,69,76,95,84,73,76,84,61,48,120,49,57,10,99,46,74,84,89,80,69,58,65,78,65,76,79,71,61,48,120,50,48,44,65,78,67,72,79,82,69,68,61,48,120,50,44,66,85,84,84,79,78,61,48,120,56,48,44,67,82,79,83,83,73,78,71,61,48,120,56,44,68,66,76,95,67,76,73,67,75,61,48,120,50,48,48,44,68,73,71,73,84,65,76,61,48,120,49,48,44,68,82,65,71,71,69,68,61,48,120,52,44,68,82,65,71,95,73,84,69,77,61,48,120,56,48,48,44,69,88,84,95,77,79,86,69,77,69,78,84,61,48,120,52,48,44,77,79,86,69,77,69,78,84,61,48,120,49,48,48,44,82,69,80,69,65,84,69,68,61,48,120,52,48,48,44,83,69,67,79,78,68,65,82,89,61,48,120,49,10,99,46,75,69,89,58,65,61,48,120,49,44,65,80,79,83,84,82,79,80,72,69,61,48,120,50,98,44,65,84,61,48,120,56,97,44,66,61,48,120,50,44,66,65,67,75,61,48,120,56,54,44,66,65,67,75,83,80,65,67,69,61,48,120,54,57,44,66,65,67,75,95,83,76,65,83,72,61,48,120,50,102,44,66,82,69,65,75,61,48,120,55,98,44,67,61,48,120,51,44,67,65,76,76,61,48,120,56,55,44,67,65,77,69,82,65,61,48,120,56,57,44,67,65,78,67,69,76,61,48,120,55,97,44,67,65,80,83,95,76,79,67,75,61,48,120,52,54,44,67,76,69,65,82,61,48,120,54,101,44,67,79,77,77,65,61,48,120,50,99,44,68,61,48,120,52,44,68,69,76,69,84,69,61,48,120,54,100,44,68,79,84,61,48,120,50,100,44,68,79,87,78,61,48,120,54,49,44,69,61,48,120,53,44,69,73,71,72,84,61,48,120,50,50,44,69,78,68,61,48,120,55,50,44,69,78,68,95,67,65,76,76,61,48,120,56,56,44,69,78,84,69,82,61,48,120,54,98,44,69,81,85,65,76,83,61,48,120,50,55,44,69,83,67,65,80,69,61,48,120,54,99,44,69,88,69,67,85,84,69,61,48,120,55,52,44,70,61,48,120,54,44,70,49,61,48,120,52,99,44,70,49,48,61,48,120,53,53,44,70,49,49,61,48,120,53,54,44,70,49,50,61,48,120,53,55,44,70,49,51,61,48,120,53,56,44,70,49,52,61,48,120,53,57,44,70,49,53,61,48,120,53,97,44,70,49,54,61,48,120,53,98,44,70,49,55,61,48,120,53,99,44,70,49,56,61,48,120,56,48,44,70,49,57,61,48,120,56,49,44,70,50,61,48,120,52,100,44,70,50,48,61,48,120,56,50,44,70,51,61,48,120,52,101,44,70,52,61,48,120,52,102,44,70,53,61,48,120,53,48,44,70,54,61,48,120,53,49,44,70,55,61,48,120,53,50,44,70,56,61,48,120,53,51,44,70,57,61,48,120,53,52,44,70,73,78,68,61,48,120,55,57,44,70,73,86,69,61,48,120,49,102,44,70,79,82,87,65,82,68,61,48,120,57,48,44,70,79,85,82,61,48,120,49,101,44,71,61,48,120,55,44,72,61,48,120,56,44,72,69,76,80,61,48,120,52,51,44,72,79,77,69,61,48,120,54,102,44,73,61,48,120,57,44,73,78,83,69,82,84,61,48,120,55,53,44,74,61,48,120,97,44,75,61,48,120,98,44,76,61,48,120,99,44,76,69,70,84,61,48,120,54,51,44,76,69,78,83,95,70,79,67,85,83,61,48,120,56,99,44,76,69,83,83,95,71,82,69,65,84,69,82,61,48,120,53,102,44,76,73,83,84,95,69,78,68,61,48,120,57,54,44,76,95,65,76,84,61,48,120,52,56,44,76,95,67,79,77,77,65,78,68,61,48,120,52,97,44,76,95,67,79,78,84,82,79,76,61,48,120,52,49,44,76,95,83,72,73,70,84,61,48,120,52,52,44,76,95,83,81,85,65,82,69,61,48,120,50,56,44,77,61,48,120,100,44,77,65,67,82,79,61,48,120,53,100,44,77,69,78,85,61,48,120,55,56,44,77,73,78,85,83,61,48,120,50,54,44,77,85,84,69,61,48,120,57,50,44,78,61,48,120,101,44,78,69,88,84,61,48,120,56,101,44,78,73,78,69,61,48,120,50,51,44,78,80,95,48,61,48,120,51,49,44,78,80,95,49,61,48,120,51,50,44,78,80,95,50,61,48,120,51,51,44,78,80,95,51,61,48,120,51,52,44,78,80,95,52,61,48,120,51,53,44,78,80,95,53,61,48,120,51,54,44,78,80,95,54,61,48,120,51,55,44,78,80,95,55,61,48,120,51,56,44,78,80,95,56,61,48,120,51,57,44,78,80,95,57,61,48,120,51,97,44,78,80,95,66,65,82,61,48,120,51,100,44,78,80,95,68,69,67,73,77,65,76,61,48,120,51,102,44,78,80,95,68,73,86,73,68,69,61,48,120,52,48,44,78,80,95,68,79,84,61,48,120,51,102,44,78,80,95,69,78,84,69,82,61,48,120,55,101,44,78,80,95,77,73,78,85,83,61,48,120,51,101,44,78,80,95,77,85,76,84,73,80,76,89,61,48,120,51,98,44,78,80,95,80,76,85,83,61,48,120,51,99,44,78,80,95,80,76,85,83,95,77,73,78,85,83,61,48,120,53,101,44,78,80,95,83,69,80,65,82,65,84,79,82,61,48,120,51,100,44,78,85,77,95,76,79,67,75,61,48,120,55,99,44,79,61,48,120,102,44,79,78,69,61,48,120,49,98,44,80,61,48,120,49,48,44,80,65,71,69,95,68,79,87,78,61,48,120,55,49,44,80,65,71,69,95,85,80,61,48,120,55,48,44,80,65,85,83,69,61,48,120,54,53,44,80,69,82,73,79,68,61,48,120,50,100,44,80,76,65,89,61,48,120,57,53,44,80,76,85,83,61,48,120,56,98,44,80,79,85,78,68,61,48,120,57,52,44,80,79,87,69,82,61,48,120,54,56,44,80,82,69,86,73,79,85,83,61,48,120,56,102,44,80,82,73,78,84,61,48,120,52,55,44,80,82,84,95,83,67,82,61,48,120,55,100,44,81,61,48,120,49,49,44,82,61,48,120,49,50,44,82,69,68,79,61,48,120,55,55,44,82,69,86,69,82,83,69,95,81,85,79,84,69,61,48,120,50,53,44,82,69,87,73,78,68,61,48,120,57,49,44,82,73,71,72,84,61,48,120,54,50,44,82,95,65,76,84,61,48,120,52,57,44,82,95,67,79,77,77,65,78,68,61,48,120,52,98,44,82,95,67,79,78,84,82,79,76,61,48,120,52,50,44,82,95,83,72,73,70,84,61,48,120,52,53,44,82,95,83,81,85,65,82,69,61,48,120,50,57,44,83,61,48,120,49,51,44,83,67,82,95,76,79,67,75,61,48,120,54,52,44,83,69,76,69,67,84,61,48,120,55,51,44,83,69,77,73,95,67,79,76,79,78,61,48,120,50,97,44,83,69,86,69,78,61,48,120,50,49,44,83,73,88,61,48,120,50,48,44,83,76,65,83,72,61,48,120,50,101,44,83,76,69,69,80,61,48,120,54,55,44,83,80,65,67,69,61,48,120,51,48,44,83,84,65,82,61,48,120,57,51,44,83,84,79,80,61,48,120,56,100,44,83,89,83,82,81,61,48,120,55,102,44,84,61,48,120,49,52,44,84,65,66,61,48,120,54,97,44,84,72,82,69,69,61,48,120,49,100,44,84,87,79,61,48,120,49,99,44,85,61,48,120,49,53,44,85,78,68,79,61,48,120,55,54,44,85,80,61,48,120,54,48,44,86,61,48,120,49,54,44,86,79,76,85,77,69,95,68,79,87,78,61,48,120,56,53,44,86,79,76,85,77,69,95,85,80,61,48,120,56,52,44,87,61,48,120,49,55,44,87,65,75,69,61,48,120,54,54,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,56,51,44,88,61,48,120,49,56,44,89,61,48,120,49,57,44,90,61,48,120,49,97,44,90,69,82,79,61,48,120,50,52,10,99,46,75,81,58,65,76,84,61,48,120,54,48,44,65,76,84,71,82,61,48,120,52,48,44,67,65,80,83,95,76,79,67,75,61,48,120,52,44,67,79,77,77,65,78,68,61,48,120,49,56,48,44,67,79,78,84,82,79,76,61,48,120,49,56,44,67,84,82,76,61,48,120,49,56,44,68,69,65,68,95,75,69,89,61,48,120,49,48,48,48,48,44,73,78,70,79,61,48,120,51,99,48,52,44,73,78,83,84,82,85,67,84,73,79,78,95,75,69,89,83,61,48,120,55,56,44,76,95,65,76,84,61,48,120,50,48,44,76,95,67,79,77,77,65,78,68,61,48,120,56,48,44,76,95,67,79,78,84,82,79,76,61,48,120,56,44,76,95,67,84,82,76,61,48,120,56,44,76,95,83,72,73,70,84,61,48,120,49,44,78,79,84,95,80,82,73,78,84,65,66,76,69,61,48,120,50,48,48,48,44,78,85,77,95,76,79,67,75,61,48,120,56,48,48,48,44,78,85,77,95,80,65,68,61,48,120,50,48,48,44,80,82,69,83,83,69,68,61,48,120,49,48,48,48,44,81,85,65,76,73,70,73,69,82,83,61,48,120,49,102,98,44,82,69,76,69,65,83,69,68,61,48,120,56,48,48,44,82,69,80,69,65,84,61,48,120,52,48,48,44,82,95,65,76,84,61,48,120,52,48,44,82,95,67,79,77,77,65,78,68,61,48,120,49,48,48,44,82,95,67,79,78,84,82,79,76,61,48,120,49,48,44,82,95,67,84,82,76,61,48,120,49,48,44,82,95,83,72,73,70,84,61,48,120,50,44,83,67,82,95,76,79,67,75,61,48,120,52,48,48,48,44,83,72,73,70,84,61,48,120,51,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,50,48,48,48,48,10,99,46,76,65,89,79,85,84,58,66,65,67,75,71,82,79,85,78,68,61,48,120,56,44,69,77,66,69,68,68,69,68,61,48,120,50,48,44,70,79,82,69,71,82,79,85,78,68,61,48,120,49,48,44,73,71,78,79,82,69,95,67,85,82,83,79,82,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,76,79,67,75,61,48,120,52,48,44,82,73,71,72,84,61,48,120,52,44,83,81,85,65,82,69,61,48,120,48,44,84,73,71,72,84,61,48,120,49,44,84,73,76,69,61,48,120,49,48,48,44,87,73,68,69,61,48,120,54,10,99,46,76,68,70,58,67,72,69,67,75,95,69,88,73,83,84,83,61,48,120,49,10,99,46,76,79,67,58,68,73,82,69,67,84,79,82,89,61,48,120,49,44,70,73,76,69,61,48,120,51,44,70,79,76,68,69,82,61,48,120,49,44,86,79,76,85,77,69,61,48,120,50,10,99,46,77,65,88,58,70,73,76,69,78,65,77,69,61,48,120,49,48,48,44,78,65,77,69,95,76,69,78,61,48,120,49,102,10,99,46,77,69,77,58,65,85,68,73,79,61,48,120,56,44,67,65,76,76,69,82,61,48,120,56,48,48,48,48,48,44,67,79,68,69,61,48,120,49,48,44,67,79,76,76,69,67,84,61,48,120,49,48,48,48,44,68,65,84,65,61,48,120,48,44,69,88,67,76,85,83,73,86,69,61,48,120,56,48,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,48,48,44,77,65,78,65,71,69,68,61,48,120,49,44,78,79,95,66,76,79,67,75,61,48,120,50,48,48,48,44,78,79,95,66,76,79,67,75,73,78,71,61,48,120,50,48,48,48,44,78,79,95,67,76,69,65,82,61,48,120,52,48,48,48,48,44,78,79,95,76,79,67,75,61,48,120,52,48,48,44,78,79,95,80,79,79,76,61,48,120,50,48,44,79,66,74,69,67,84,61,48,120,50,48,48,44,82,69,65,68,61,48,120,49,48,48,48,48,44,82,69,65,68,95,87,82,73,84,69,61,48,120,51,48,48,48,48,44,83,84,82,73,78,71,61,48,120,49,48,48,44,84,69,88,84,85,82,69,61,48,120,52,44,84,77,80,95,76,79,67,75,61,48,120,52,48,44,85,78,84,82,65,67,75,69,68,61,48,120,56,48,44,86,73,68,69,79,61,48,120,50,44,87,82,73,84,69,61,48,120,50,48,48,48,48,10,99,46,77,70,70,58,65,84,84,82,73,66,61,48,120,50,48,44,67,76,79,83,69,68,61,48,120,56,48,44,67,82,69,65,84,69,61,48,120,52,44,68,69,69,80,61,48,120,49,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,70,73,76,69,61,48,120,52,48,48,44,70,79,76,68,69,82,61,48,120,50,48,48,44,77,79,68,73,70,89,61,48,120,50,44,77,79,86,69,68,61,48,120,49,48,44,79,80,69,78,69,68,61,48,120,52,48,44,82,69,65,68,61,48,120,49,44,82,69,78,65,77,69,61,48,120,49,48,44,83,69,76,70,61,48,120,56,48,48,44,85,78,77,79,85,78,84,61,48,120,49,48,48,44,87,82,73,84,69,61,48,120,50,10,99,46,77,72,70,58,68,69,70,65,85,76,84,61,48,120,50,44,83,84,65,84,73,67,61,48,120,49,44,83,84,82,85,67,84,85,82,69,61,48,120,50,10,99,46,77,79,70,58,76,73,78,75,95,76,73,66,82,65,82,89,61,48,120,49,44,83,84,65,84,73,67,61,48,120,50,44,83,89,83,84,69,77,95,80,82,79,66,69,61,48,120,52,10,99,46,77,79,86,69,58,65,76,76,61,48,120,102,44,68,79,87,78,61,48,120,49,44,76,69,70,84,61,48,120,52,44,82,73,71,72,84,61,48,120,56,44,85,80,61,48,120,50,10,99,46,77,83,70,58,65,68,68,61,48,120,56,44,65,68,68,82,69,83,83,61,48,120,49,48,44,77,69,83,83,65,71,69,95,73,68,61,48,120,50,48,44,78,79,95,68,85,80,76,73,67,65,84,69,61,48,120,52,44,85,80,68,65,84,69,61,48,120,50,44,87,65,73,84,61,48,120,49,10,99,46,77,83,71,73,68,58,65,67,84,73,79,78,61,48,120,54,51,44,66,82,69,65,75,61,48,120,54,52,44,67,79,77,77,65,78,68,61,48,120,54,53,44,67,79,82,69,95,69,78,68,61,48,120,54,52,44,68,69,66,85,71,61,48,120,53,102,44,69,86,69,78,84,61,48,120,53,101,44,70,82,69,69,61,48,120,54,50,44,81,85,73,84,61,48,120,51,101,56,44,84,72,82,69,65,68,95,65,67,84,73,79,78,61,48,120,53,98,44,84,72,82,69,65,68,95,67,65,76,76,66,65,67,75,61,48,120,53,99,44,86,65,76,73,68,65,84,69,95,80,82,79,67,69,83,83,61,48,120,53,100,44,87,65,73,84,95,70,79,82,95,79,66,74,69,67,84,83,61,48,120,53,97,10,99,46,77,84,70,58,65,78,73,77,61,48,120,56,44,82,69,76,65,84,73,86,69,61,48,120,49,48,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,78,69,84,77,83,71,58,69,78,68,61,48,120,49,44,83,84,65,82,84,61,48,120,48,10,99,46,78,70,58,67,79,76,76,69,67,84,61,48,120,56,48,44,70,82,69,69,61,48,120,49,48,44,70,82,69,69,95,79,78,95,85,78,76,79,67,75,61,48,120,56,44,73,78,73,84,73,65,76,73,83,69,68,61,48,120,50,44,76,79,67,65,76,61,48,120,52,44,77,69,83,83,65,71,69,61,48,120,50,48,48,44,78,65,77,69,61,48,120,56,48,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,48,44,82,69,67,76,65,83,83,69,68,61,48,120,49,48,48,44,83,73,71,78,65,76,76,69,68,61,48,120,52,48,48,44,83,85,80,80,82,69,83,83,95,76,79,71,61,48,120,52,48,44,84,73,77,69,82,95,83,85,66,61,48,120,50,48,44,85,78,73,81,85,69,61,48,120,52,48,48,48,48,48,48,48,44,85,78,84,82,65,67,75,69,68,61,48,120,49,10,99,46,79,80,70,58,65,82,71,83,61,48,120,52,48,44,68,69,84,65,73,76,61,48,120,52,44,69,82,82,79,82,61,48,120,56,48,44,77,65,88,95,68,69,80,84,72,61,48,120,50,44,77,79,68,85,76,69,95,80,65,84,72,61,48,120,52,48,48,44,79,80,84,73,79,78,83,61,48,120,49,44,80,82,73,86,73,76,69,71,69,68,61,48,120,49,48,48,44,82,79,79,84,95,80,65,84,72,61,48,120,56,48,48,44,83,67,65,78,95,77,79,68,85,76,69,83,61,48,120,49,48,48,48,44,83,72,79,87,95,69,82,82,79,82,83,61,48,120,50,48,44,83,72,79,87,95,73,79,61,48,120,49,48,44,83,72,79,87,95,77,69,77,79,82,89,61,48,120,56,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,48,48,10,99,46,80,69,82,77,73,84,58,65,76,76,95,68,69,76,69,84,69,61,48,120,56,56,56,44,65,76,76,95,69,88,69,67,61,48,120,52,52,52,44,65,76,76,95,82,69,65,68,61,48,120,49,49,49,44,65,76,76,95,87,82,73,84,69,61,48,120,50,50,50,44,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,69,86,69,82,89,79,78,69,95,65,67,67,69,83,83,61,48,120,102,102,102,44,69,86,69,82,89,79,78,69,95,68,69,76,69,84,69,61,48,120,56,56,56,44,69,86,69,82,89,79,78,69,95,69,88,69,67,61,48,120,52,52,52,44,69,86,69,82,89,79,78,69,95,82,69,65,68,61,48,120,49,49,49,44,69,86,69,82,89,79,78,69,95,82,69,65,68,87,82,73,84,69,61,48,120,51,51,51,44,69,86,69,82,89,79,78,69,95,87,82,73,84,69,61,48,120,50,50,50,44,69,88,69,67,61,48,120,52,44,71,82,79,85,80,61,48,120,102,48,44,71,82,79,85,80,73,68,61,48,120,49,48,48,48,48,44,71,82,79,85,80,95,68,69,76,69,84,69,61,48,120,56,48,44,71,82,79,85,80,95,69,88,69,67,61,48,120,52,48,44,71,82,79,85,80,95,82,69,65,68,61,48,120,49,48,44,71,82,79,85,80,95,87,82,73,84,69,61,48,120,50,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,44,73,78,72,69,82,73,84,61,48,120,50,48,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,48,48,48,44,79,70,70,76,73,78,69,61,48,120,52,48,48,48,48,44,79,84,72,69,82,83,61,48,120,102,48,48,44,79,84,72,69,82,83,95,68,69,76,69,84,69,61,48,120,56,48,48,44,79,84,72,69,82,83,95,69,88,69,67,61,48,120,52,48,48,44,79,84,72,69,82,83,95,82,69,65,68,61,48,120,49,48,48,44,79,84,72,69,82,83,95,87,82,73,84,69,61,48,120,50,48,48,44,80,65,83,83,87,79,82,68,61,48,120,52,48,48,48,44,82,69,65,68,61,48,120,49,44,85,83,69,82,61,48,120,102,44,85,83,69,82,73,68,61,48,120,56,48,48,48,44,85,83,69,82,95,69,88,69,67,61,48,120,52,44,85,83,69,82,95,82,69,65,68,61,48,120,49,44,85,83,69,82,95,87,82,73,84,69,61,48,120,50,44,87,82,73,84,69,61,48,120,50,10,99,46,80,77,70,58,83,89,83,84,69,77,95,78,79,95,66,82,69,65,75,61,48,120,49,10,99,46,80,84,67,58,67,82,79,83,83,72,65,73,82,61,48,120,97,44,67,85,83,84,79,77,61,48,120,49,55,44,68,69,70,65,85,76,84,61,48,120,49,44,68,82,65,71,71,65,66,76,69,61,48,120,49,56,44,69,78,68,61,48,120,49,57,44,72,65,78,68,61,48,120,49,48,44,72,65,78,68,95,76,69,70,84,61,48,120,49,49,44,72,65,78,68,95,82,73,71,72,84,61,48,120,49,50,44,73,78,86,73,83,73,66,76,69,61,48,120,49,54,44,77,65,71,78,73,70,73,69,82,61,48,120,102,44,78,79,95,67,72,65,78,71,69,61,48,120,48,44,80,65,73,78,84,66,82,85,83,72,61,48,120,49,52,44,83,73,90,69,95,66,79,84,84,79,77,61,48,120,57,44,83,73,90,69,95,66,79,84,84,79,77,95,76,69,70,84,61,48,120,50,44,83,73,90,69,95,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,51,44,83,73,90,69,95,76,69,70,84,61,48,120,54,44,83,73,90,69,95,82,73,71,72,84,61,48,120,55,44,83,73,90,69,95,84,79,80,61,48,120,56,44,83,73,90,69,95,84,79,80,95,76,69,70,84,61,48,120,52,44,83,73,90,69,95,84,79,80,95,82,73,71,72,84,61,48,120,53,44,83,73,90,73,78,71,61,48,120,99,44,83,76,69,69,80,61,48,120,98,44,83,80,76,73,84,95,72,79,82,73,90,79,78,84,65,76,61,48,120,101,44,83,80,76,73,84,95,86,69,82,84,73,67,65,76,61,48,120,100,44,83,84,79,80,61,48,120,49,53,44,84,69,88,84,61,48,120,49,51,10,99,46,82,68,70,58,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,65,84,69,61,48,120,50,44,70,73,76,69,61,48,120,56,44,70,73,76,69,83,61,48,120,56,44,70,79,76,68,69,82,61,48,120,49,48,44,70,79,76,68,69,82,83,61,48,120,49,48,44,72,73,68,68,69,78,61,48,120,49,48,48,44,76,73,78,75,61,48,120,52,48,44,79,80,69,78,68,73,82,61,48,120,52,48,48,48,44,80,69,82,77,73,83,83,73,79,78,83,61,48,120,52,44,81,85,65,76,73,70,73,69,68,61,48,120,50,48,48,44,81,85,65,76,73,70,89,61,48,120,50,48,48,44,82,69,65,68,95,65,76,76,61,48,120,49,102,44,82,69,65,68,95,79,78,76,89,61,48,120,49,48,48,48,44,83,73,90,69,61,48,120,49,44,83,84,82,69,65,77,61,48,120,56,48,48,44,84,65,71,83,61,48,120,56,48,44,84,73,77,69,61,48,120,50,44,86,73,82,84,85,65,76,61,48,120,52,48,48,44,86,79,76,85,77,69,61,48,120,50,48,10,99,46,82,69,83,58,67,79,78,83,79,76,69,95,70,68,61,48,120,50,44,67,79,82,69,95,73,68,76,61,48,120,56,44,67,80,85,95,83,80,69,69,68,61,48,120,49,54,44,68,73,83,80,76,65,89,95,68,82,73,86,69,82,61,48,120,53,44,69,88,67,69,80,84,73,79,78,95,72,65,78,68,76,69,82,61,48,120,49,49,44,70,82,69,69,95,77,69,77,79,82,89,61,48,120,49,55,44,70,82,69,69,95,83,87,65,80,61,48,120,49,44,74,78,73,95,69,78,86,61,48,120,101,44,75,69,89,95,83,84,65,84,69,61,48,120,51,44,76,79,71,95,68,69,80,84,72,61,48,120,100,44,76,79,71,95,76,69,86,69,76,61,48,120,97,44,77,65,88,95,80,82,79,67,69,83,83,69,83,61,48,120,99,44,78,69,84,95,80,82,79,67,69,83,83,73,78,71,61,48,120,49,50,44,79,80,69,78,95,73,78,70,79,61,48,120,49,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,55,44,80,82,73,86,73,76,69,71,69,68,95,85,83,69,82,61,48,120,54,44,80,82,79,67,69,83,83,95,83,84,65,84,69,61,48,120,49,51,44,83,84,65,84,73,67,95,66,85,73,76,68,61,48,120,57,44,84,72,82,69,65,68,95,73,68,61,48,120,102,44,84,79,84,65,76,95,77,69,77,79,82,89,61,48,120,49,52,44,84,79,84,65,76,95,83,72,65,82,69,68,95,77,69,77,79,82,89,61,48,120,98,44,84,79,84,65,76,95,83,87,65,80,61,48,120,49,53,44,85,83,69,82,95,73,68,61,48,120,52,10,99,46,82,70,68,58,65,76,76,79,87,95,82,69,67,85,82,83,73,79,78,61,48,120,50,48,44,65,76,87,65,89,83,95,67,65,76,76,61,48,120,49,48,48,44,69,88,67,69,80,84,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,67,65,76,76,61,48,120,56,48,44,82,69,77,79,86,69,61,48,120,56,44,83,79,67,75,69,84,61,48,120,52,48,44,83,84,79,80,95,82,69,67,85,82,83,69,61,48,120,49,48,44,87,82,73,84,69,61,48,120,49,10,99,46,82,80,58,77,79,68,85,76,69,95,80,65,84,72,61,48,120,49,44,82,79,79,84,95,80,65,84,72,61,48,120,51,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,10,99,46,82,83,70,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,52,44,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,50,48,44,67,72,69,67,75,95,86,73,82,84,85,65,76,61,48,120,50,44,78,79,95,68,69,69,80,95,83,67,65,78,61,48,120,56,44,78,79,95,70,73,76,69,95,67,72,69,67,75,61,48,120,49,44,80,65,84,72,61,48,120,49,48,10,99,46,83,67,70,58,69,88,73,84,95,79,78,95,69,82,82,79,82,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,10,99,46,83,69,69,75,58,67,85,82,82,69,78,84,61,48,120,49,44,69,78,68,61,48,120,50,44,82,69,76,65,84,73,86,69,61,48,120,51,44,83,84,65,82,84,61,48,120,48,10,99,46,83,84,82,58,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,76,69,78,61,48,120,50,10,99,46,83,84,84,58,70,76,79,65,84,61,48,120,50,44,72,69,88,61,48,120,51,44,78,85,77,66,69,82,61,48,120,49,44,83,84,82,73,78,71,61,48,120,52,10,99,46,84,72,70,58,65,85,84,79,95,70,82,69,69,61,48,120,49,10,99,46,84,79,73,58,65,78,68,82,79,73,68,95,65,83,83,69,84,77,71,82,61,48,120,52,44,65,78,68,82,79,73,68,95,67,76,65,83,83,61,48,120,51,44,65,78,68,82,79,73,68,95,69,78,86,61,48,120,50,44,76,79,67,65,76,95,67,65,67,72,69,61,48,120,48,44,76,79,67,65,76,95,83,84,79,82,65,71,69,61,48,120,49,10,99,46,84,83,70,58,65,84,84,65,67,72,69,68,61,48,120,49,48,48,44,68,69,84,65,67,72,69,68,61,48,120,56,48,44,70,79,82,69,73,71,78,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,48,44,80,73,80,69,61,48,120,50,48,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,56,44,81,85,73,69,84,61,48,120,52,48,44,82,69,83,69,84,95,80,65,84,72,61,48,120,52,44,83,72,69,76,76,61,48,120,49,48,44,87,65,73,84,61,48,120,50,10,99,46,84,83,84,65,84,69,58,80,65,85,83,69,68,61,48,120,49,44,82,85,78,78,73,78,71,61,48,120,48,44,83,84,79,80,80,73,78,71,61,48,120,50,44,84,69,82,77,73,78,65,84,69,68,61,48,120,51,10,99,46,86,65,83,58,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,102,44,67,76,79,83,69,95,68,73,82,61,48,120,54,44,67,82,69,65,84,69,95,76,73,78,75,61,48,120,49,49,44,68,69,76,69,84,69,61,48,120,51,44,68,69,82,69,71,73,83,84,69,82,61,48,120,49,44,68,82,73,86,69,82,95,83,73,90,69,61,48,120,49,50,44,71,69,84,95,68,69,86,73,67,69,95,73,78,70,79,61,48,120,98,44,71,69,84,95,73,78,70,79,61,48,120,97,44,73,68,69,78,84,73,70,89,95,70,73,76,69,61,48,120,99,44,73,71,78,79,82,69,95,70,73,76,69,61,48,120,57,44,77,65,75,69,95,68,73,82,61,48,120,100,44,79,80,69,78,95,68,73,82,61,48,120,53,44,82,69,65,68,95,76,73,78,75,61,48,120,49,48,44,82,69,78,65,77,69,61,48,120,52,44,83,65,77,69,95,70,73,76,69,61,48,120,101,44,83,67,65,78,95,68,73,82,61,48,120,50,44,84,69,83,84,95,80,65,84,72,61,48,120,55,44,87,65,84,67,72,95,80,65,84,72,61,48,120,56,10,99,46,86,76,70,58,65,80,73,61,48,120,50,48,44,66,82,65,78,67,72,61,48,120,49,44,67,82,73,84,73,67,65,76,61,48,120,56,44,68,69,84,65,73,76,61,48,120,52,48,44,69,82,82,79,82,61,48,120,50,44,70,85,78,67,84,73,79,78,61,48,120,49,48,48,44,73,78,70,79,61,48,120,49,48,44,84,82,65,67,69,61,48,120,56,48,44,87,65,82,78,73,78,71,61,48,120,52,10,99,46,86,79,76,85,77,69,58,72,73,68,68,69,78,61,48,120,52,44,80,82,73,79,82,73,84,89,61,48,120,50,44,82,69,80,76,65,67,69,61,48,120,49,44,83,89,83,84,69,77,61,48,120,56,10,0 }; +char glIDL[] = { 115,46,73,110,112,117,116,69,118,101,110,116,58,112,78,101,120,116,58,73,110,112,117,116,69,118,101,110,116,44,100,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,82,101,99,105,112,105,101,110,116,73,68,44,108,79,118,101,114,73,68,44,100,65,98,115,88,44,100,65,98,115,89,44,100,88,44,100,89,44,108,68,101,118,105,99,101,73,68,44,108,84,121,112,101,44,108,70,108,97,103,115,44,108,77,97,115,107,10,115,46,100,99,82,101,113,117,101,115,116,58,108,73,116,101,109,44,99,80,114,101,102,101,114,101,110,99,101,91,52,93,10,115,46,100,99,65,117,100,105,111,58,108,83,105,122,101,44,108,70,111,114,109,97,116,10,115,46,100,99,75,101,121,69,110,116,114,121,58,108,70,108,97,103,115,44,108,86,97,108,117,101,44,120,84,105,109,101,115,116,97,109,112,44,108,85,110,105,99,111,100,101,10,115,46,100,99,68,101,118,105,99,101,73,110,112,117,116,58,100,86,97,108,117,101,115,91,50,93,44,120,84,105,109,101,115,116,97,109,112,44,108,68,101,118,105,99,101,73,68,44,108,70,108,97,103,115,44,108,84,121,112,101,10,115,46,68,97,116,101,84,105,109,101,58,119,89,101,97,114,44,99,77,111,110,116,104,44,99,68,97,121,44,99,72,111,117,114,44,99,77,105,110,117,116,101,44,99,83,101,99,111,110,100,44,99,84,105,109,101,90,111,110,101,10,115,46,72,83,86,58,100,72,117,101,44,100,83,97,116,117,114,97,116,105,111,110,44,100,86,97,108,117,101,44,100,65,108,112,104,97,10,115,46,70,82,71,66,58,102,82,101,100,44,102,71,114,101,101,110,44,102,66,108,117,101,44,102,65,108,112,104,97,10,115,46,82,71,66,56,58,117,99,82,101,100,44,117,99,71,114,101,101,110,44,117,99,66,108,117,101,44,117,99,65,108,112,104,97,10,115,46,82,71,66,49,54,58,117,119,82,101,100,44,117,119,71,114,101,101,110,44,117,119,66,108,117,101,44,117,119,65,108,112,104,97,10,115,46,82,71,66,51,50,58,117,108,82,101,100,44,117,108,71,114,101,101,110,44,117,108,66,108,117,101,44,117,108,65,108,112,104,97,10,115,46,82,71,66,80,97,108,101,116,116,101,58,108,65,109,116,67,111,108,111,117,114,115,44,101,67,111,108,58,82,71,66,56,91,50,53,54,93,10,115,46,67,111,108,111,117,114,70,111,114,109,97,116,58,117,99,82,101,100,83,104,105,102,116,44,117,99,71,114,101,101,110,83,104,105,102,116,44,117,99,66,108,117,101,83,104,105,102,116,44,117,99,65,108,112,104,97,83,104,105,102,116,44,117,99,82,101,100,77,97,115,107,44,117,99,71,114,101,101,110,77,97,115,107,44,117,99,66,108,117,101,77,97,115,107,44,117,99,65,108,112,104,97,77,97,115,107,44,117,99,82,101,100,80,111,115,44,117,99,71,114,101,101,110,80,111,115,44,117,99,66,108,117,101,80,111,115,44,117,99,65,108,112,104,97,80,111,115,44,117,99,66,105,116,115,80,101,114,80,105,120,101,108,10,115,46,67,108,105,112,82,101,99,116,97,110,103,108,101,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,69,100,103,101,115,58,108,76,101,102,116,44,108,84,111,112,44,108,82,105,103,104,116,44,108,66,111,116,116,111,109,10,115,46,79,98,106,101,99,116,83,105,103,110,97,108,58,111,79,98,106,101,99,116,10,115,46,70,117,110,99,116,105,111,110,70,105,101,108,100,58,115,78,97,109,101,44,117,108,84,121,112,101,10,115,46,70,117,110,99,116,105,111,110,58,112,65,100,100,114,101,115,115,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,70,105,101,108,100,65,114,114,97,121,58,115,78,97,109,101,44,112,71,101,116,70,105,101,108,100,44,112,83,101,116,70,105,101,108,100,44,109,65,114,103,44,117,108,70,108,97,103,115,10,115,46,70,105,101,108,100,68,101,102,58,115,78,97,109,101,44,108,86,97,108,117,101,10,115,46,83,121,115,116,101,109,83,116,97,116,101,58,115,80,108,97,116,102,111,114,109,44,108,67,111,110,115,111,108,101,70,68,44,108,83,116,97,103,101,10,115,46,85,110,105,116,58,100,86,97,108,117,101,44,117,108,84,121,112,101,10,115,46,65,99,116,105,111,110,65,114,114,97,121,58,112,82,111,117,116,105,110,101,44,108,65,99,116,105,111,110,67,111,100,101,10,115,46,65,99,116,105,111,110,84,97,98,108,101,58,117,108,72,97,115,104,44,108,83,105,122,101,44,115,78,97,109,101,44,112,65,114,103,115,58,70,117,110,99,116,105,111,110,70,105,101,108,100,10,115,46,67,104,105,108,100,69,110,116,114,121,58,108,79,98,106,101,99,116,73,68,44,117,108,67,108,97,115,115,73,68,10,115,46,77,101,115,115,97,103,101,58,120,84,105,109,101,44,108,85,73,68,44,108,84,121,112,101,44,108,83,105,122,101,10,115,46,77,101,109,73,110,102,111,58,112,83,116,97,114,116,44,108,79,98,106,101,99,116,73,68,44,117,108,83,105,122,101,44,108,70,108,97,103,115,44,108,77,101,109,111,114,121,73,68,44,119,65,99,99,101,115,115,67,111,117,110,116,10,115,46,67,111,109,112,114,101,115,115,105,111,110,70,101,101,100,98,97,99,107,58,108,70,101,101,100,98,97,99,107,73,68,44,108,73,110,100,101,120,44,115,80,97,116,104,44,115,68,101,115,116,44,120,80,114,111,103,114,101,115,115,44,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,119,89,101,97,114,44,119,77,111,110,116,104,44,119,68,97,121,44,119,72,111,117,114,44,119,77,105,110,117,116,101,44,119,83,101,99,111,110,100,10,115,46,67,111,109,112,114,101,115,115,101,100,73,116,101,109,58,120,79,114,105,103,105,110,97,108,83,105,122,101,44,120,67,111,109,112,114,101,115,115,101,100,83,105,122,101,44,112,78,101,120,116,58,67,111,109,112,114,101,115,115,101,100,73,116,101,109,44,115,80,97,116,104,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,108,79,116,104,101,114,115,73,68,44,108,70,108,97,103,115,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,70,105,108,101,73,110,102,111,58,120,83,105,122,101,44,120,84,105,109,101,83,116,97,109,112,44,112,78,101,120,116,58,70,105,108,101,73,110,102,111,44,115,78,97,109,101,44,108,70,108,97,103,115,44,108,80,101,114,109,105,115,115,105,111,110,115,44,108,85,115,101,114,73,68,44,108,71,114,111,117,112,73,68,44,101,67,114,101,97,116,101,100,58,68,97,116,101,84,105,109,101,44,101,77,111,100,105,102,105,101,100,58,68,97,116,101,84,105,109,101,10,115,46,68,105,114,73,110,102,111,58,112,73,110,102,111,58,70,105,108,101,73,110,102,111,10,115,46,70,105,108,101,70,101,101,100,98,97,99,107,58,120,83,105,122,101,44,120,80,111,115,105,116,105,111,110,44,115,80,97,116,104,44,115,68,101,115,116,44,108,70,101,101,100,98,97,99,107,73,68,44,99,82,101,115,101,114,118,101,100,91,51,50,93,10,115,46,70,105,101,108,100,58,109,65,114,103,44,112,71,101,116,86,97,108,117,101,44,112,83,101,116,86,97,108,117,101,44,112,87,114,105,116,101,86,97,108,117,101,44,115,78,97,109,101,44,117,108,70,105,101,108,100,73,68,44,117,119,79,102,102,115,101,116,44,117,119,73,110,100,101,120,44,117,108,70,108,97,103,115,10,99,46,65,67,58,65,99,116,105,118,97,116,101,61,48,120,50,44,67,108,101,97,114,61,48,120,52,44,67,108,105,112,98,111,97,114,100,61,48,120,50,100,44,67,111,112,121,68,97,116,97,61,48,120,55,44,68,97,116,97,70,101,101,100,61,48,120,56,44,68,101,97,99,116,105,118,97,116,101,61,48,120,57,44,68,105,115,97,98,108,101,61,48,120,50,102,44,68,114,97,103,68,114,111,112,61,48,120,49,48,44,68,114,97,119,61,48,120,97,44,69,78,68,61,48,120,51,49,44,69,110,97,98,108,101,61,48,120,54,44,70,108,117,115,104,61,48,120,98,44,70,111,99,117,115,61,48,120,99,44,70,114,101,101,61,48,120,100,44,70,114,101,101,87,97,114,110,105,110,103,61,48,120,53,44,71,101,116,75,101,121,61,48,120,102,44,72,105,100,101,61,48,120,49,49,44,73,110,105,116,61,48,120,49,50,44,76,111,99,107,61,48,120,49,51,44,76,111,115,116,70,111,99,117,115,61,48,120,49,52,44,77,111,118,101,61,48,120,49,53,44,77,111,118,101,84,111,66,97,99,107,61,48,120,49,54,44,77,111,118,101,84,111,70,114,111,110,116,61,48,120,49,55,44,77,111,118,101,84,111,80,111,105,110,116,61,48,120,50,51,44,78,101,119,67,104,105,108,100,61,48,120,49,56,44,78,101,119,79,98,106,101,99,116,61,48,120,49,97,44,78,101,119,79,119,110,101,114,61,48,120,49,57,44,78,101,119,80,108,97,99,101,109,101,110,116,61,48,120,51,48,44,78,101,120,116,61,48,120,50,57,44,80,114,101,118,61,48,120,50,97,44,81,117,101,114,121,61,48,120,49,99,44,82,101,97,100,61,48,120,49,100,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,51,44,82,101,100,111,61,48,120,49,98,44,82,101,102,114,101,115,104,61,48,120,50,101,44,82,101,110,97,109,101,61,48,120,49,101,44,82,101,115,101,116,61,48,120,49,102,44,82,101,115,105,122,101,61,48,120,50,48,44,83,97,118,101,73,109,97,103,101,61,48,120,50,49,44,83,97,118,101,83,101,116,116,105,110,103,115,61,48,120,101,44,83,97,118,101,84,111,79,98,106,101,99,116,61,48,120,50,50,44,83,101,101,107,61,48,120,50,52,44,83,101,116,70,105,101,108,100,61,48,120,50,99,44,83,101,116,75,101,121,61,48,120,50,53,44,83,104,111,119,61,48,120,50,54,44,83,105,103,110,97,108,61,48,120,49,44,85,110,100,111,61,48,120,50,55,44,85,110,108,111,99,107,61,48,120,50,56,44,87,114,105,116,101,61,48,120,50,98,10,99,46,65,76,73,71,78,58,66,79,84,84,79,77,61,48,120,50,48,44,67,69,78,84,69,82,61,48,120,99,44,72,79,82,73,90,79,78,84,65,76,61,48,120,52,44,76,69,70,84,61,48,120,49,44,77,73,68,68,76,69,61,48,120,99,44,82,73,71,72,84,61,48,120,50,44,84,79,80,61,48,120,49,48,44,86,69,82,84,73,67,65,76,61,48,120,56,10,99,46,67,67,70,58,65,85,68,73,79,61,48,120,56,48,44,67,79,77,77,65,78,68,61,48,120,49,44,68,65,84,65,61,48,120,49,48,48,44,70,73,76,69,83,89,83,84,69,77,61,48,120,50,44,71,82,65,80,72,73,67,83,61,48,120,52,44,71,85,73,61,48,120,56,44,73,79,61,48,120,49,48,44,77,73,83,67,61,48,120,50,48,48,44,77,85,76,84,73,77,69,68,73,65,61,48,120,56,48,48,44,78,69,84,87,79,82,75,61,48,120,52,48,48,44,83,89,83,84,69,77,61,48,120,50,48,44,84,79,79,76,61,48,120,52,48,10,99,46,67,70,58,68,69,70,76,65,84,69,61,48,120,51,44,71,90,73,80,61,48,120,49,44,90,76,73,66,61,48,120,50,10,99,46,67,76,70,58,73,78,72,69,82,73,84,95,76,79,67,65,76,61,48,120,49,44,78,79,95,79,87,78,69,82,83,72,73,80,61,48,120,50,10,99,46,67,76,73,80,77,79,68,69,58,67,79,80,89,61,48,120,50,44,67,85,84,61,48,120,49,44,80,65,83,84,69,61,48,120,52,10,99,46,67,77,70,58,65,80,80,76,89,95,83,69,67,85,82,73,84,89,61,48,120,50,48,44,67,82,69,65,84,69,95,70,73,76,69,61,48,120,52,44,78,69,87,61,48,120,50,44,78,79,95,76,73,78,75,83,61,48,120,49,48,44,80,65,83,83,87,79,82,68,61,48,120,49,44,82,69,65,68,95,79,78,76,89,61,48,120,56,10,99,46,67,78,70,58,65,85,84,79,95,83,65,86,69,61,48,120,50,44,78,69,87,61,48,120,56,44,79,80,84,73,79,78,65,76,95,70,73,76,69,83,61,48,120,52,44,83,84,82,73,80,95,81,85,79,84,69,83,61,48,120,49,10,99,46,68,65,84,65,58,65,85,68,73,79,61,48,120,53,44,67,79,78,84,69,78,84,61,48,120,98,44,68,69,86,73,67,69,95,73,78,80,85,84,61,48,120,51,44,70,73,76,69,61,48,120,97,44,73,77,65,71,69,61,48,120,55,44,73,78,80,85,84,95,82,69,65,68,89,61,48,120,99,44,82,65,87,61,48,120,50,44,82,69,67,69,73,80,84,61,48,120,57,44,82,69,67,79,82,68,61,48,120,54,44,82,69,81,85,69,83,84,61,48,120,56,44,84,69,88,84,61,48,120,49,44,88,77,76,61,48,120,52,10,99,46,68,69,86,73,67,69,58,66,79,79,75,77,65,82,75,61,48,120,52,48,48,48,48,44,67,79,77,80,65,67,84,95,68,73,83,67,61,48,120,49,44,70,73,88,69,68,61,48,120,56,48,48,48,44,70,76,79,80,80,89,95,68,73,83,75,61,48,120,52,44,72,65,82,68,95,68,73,83,75,61,48,120,50,44,77,69,77,79,82,89,61,48,120,49,48,48,48,44,77,79,68,69,77,61,48,120,50,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,44,80,82,73,78,84,69,82,61,48,120,50,48,48,44,80,82,73,78,84,69,82,95,51,68,61,48,120,49,48,48,48,48,44,82,69,65,68,61,48,120,56,44,82,69,77,79,86,65,66,76,69,61,48,120,50,48,44,82,69,77,79,86,69,65,66,76,69,61,48,120,50,48,44,83,67,65,78,78,69,82,61,48,120,52,48,48,44,83,67,65,78,78,69,82,95,51,68,61,48,120,50,48,48,48,48,44,83,79,70,84,87,65,82,69,61,48,120,52,48,44,84,65,80,69,61,48,120,49,48,48,44,84,69,77,80,79,82,65,82,89,61,48,120,56,48,48,44,85,83,66,61,48,120,52,48,48,48,44,87,82,73,84,69,61,48,120,49,48,10,99,46,68,77,70,58,70,73,88,69,68,95,67,69,78,84,69,82,95,88,61,48,120,49,48,48,48,48,48,44,70,73,88,69,68,95,67,69,78,84,69,82,95,89,61,48,120,50,48,48,48,48,48,44,70,73,88,69,68,95,68,69,80,84,72,61,48,120,49,48,48,48,44,70,73,88,69,68,95,72,69,73,71,72,84,61,48,120,49,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,88,61,48,120,50,48,48,48,48,44,70,73,88,69,68,95,82,65,68,73,85,83,95,89,61,48,120,50,48,48,48,48,48,48,44,70,73,88,69,68,95,87,73,68,84,72,61,48,120,50,48,48,44,70,73,88,69,68,95,88,61,48,120,52,44,70,73,88,69,68,95,88,95,79,70,70,83,69,84,61,48,120,52,48,44,70,73,88,69,68,95,89,61,48,120,56,44,70,73,88,69,68,95,89,95,79,70,70,83,69,84,61,48,120,56,48,44,70,73,88,69,68,95,90,61,48,120,52,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,88,61,48,120,52,48,48,48,48,44,83,67,65,76,69,68,95,67,69,78,84,69,82,95,89,61,48,120,56,48,48,48,48,44,83,67,65,76,69,68,95,68,69,80,84,72,61,48,120,50,48,48,48,44,83,67,65,76,69,68,95,72,69,73,71,72,84,61,48,120,52,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,88,61,48,120,49,48,48,48,48,44,83,67,65,76,69,68,95,82,65,68,73,85,83,95,89,61,48,120,49,48,48,48,48,48,48,44,83,67,65,76,69,68,95,87,73,68,84,72,61,48,120,56,48,48,44,83,67,65,76,69,68,95,88,61,48,120,49,44,83,67,65,76,69,68,95,88,95,79,70,70,83,69,84,61,48,120,49,48,44,83,67,65,76,69,68,95,89,61,48,120,50,44,83,67,65,76,69,68,95,89,95,79,70,70,83,69,84,61,48,120,50,48,44,83,67,65,76,69,68,95,90,61,48,120,56,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,72,61,48,120,52,48,48,48,48,48,44,83,84,65,84,85,83,95,67,72,65,78,71,69,95,86,61,48,120,56,48,48,48,48,48,10,99,46,68,82,76,58,68,79,87,78,61,48,120,49,44,69,65,83,84,61,48,120,50,44,76,69,70,84,61,48,120,51,44,78,79,82,84,72,61,48,120,48,44,78,79,82,84,72,95,69,65,83,84,61,48,120,52,44,78,79,82,84,72,95,87,69,83,84,61,48,120,53,44,82,73,71,72,84,61,48,120,50,44,83,79,85,84,72,61,48,120,49,44,83,79,85,84,72,95,69,65,83,84,61,48,120,54,44,83,79,85,84,72,95,87,69,83,84,61,48,120,55,44,85,80,61,48,120,48,44,87,69,83,84,61,48,120,51,10,99,46,69,68,71,69,58,65,76,76,61,48,120,102,102,44,66,79,84,84,79,77,61,48,120,56,44,66,79,84,84,79,77,95,76,69,70,84,61,48,120,52,48,44,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,82,73,71,72,84,61,48,120,52,44,84,79,80,61,48,120,49,44,84,79,80,95,76,69,70,84,61,48,120,49,48,44,84,79,80,95,82,73,71,72,84,61,48,120,50,48,10,99,46,69,82,70,58,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,10,99,46,69,82,82,58,65,99,99,101,115,115,77,101,109,111,114,121,61,48,120,52,98,44,65,99,99,101,115,115,79,98,106,101,99,116,61,48,120,53,51,44,65,99,99,101,115,115,83,101,109,97,112,104,111,114,101,61,48,120,55,51,44,65,99,116,105,118,97,116,101,61,48,120,52,50,44,65,100,100,67,108,97,115,115,61,48,120,52,49,44,65,108,108,111,99,77,101,109,111,114,121,61,48,120,53,52,44,65,108,108,111,99,83,101,109,97,112,104,111,114,101,61,48,120,55,50,44,65,108,114,101,97,100,121,68,101,102,105,110,101,100,61,48,120,98,54,44,65,108,114,101,97,100,121,76,111,99,107,101,100,61,48,120,57,99,44,65,114,103,115,61,48,120,49,52,44,65,114,114,97,121,70,117,108,108,61,48,120,50,100,44,66,117,102,102,101,114,79,118,101,114,102,108,111,119,61,48,120,53,99,44,66,117,115,121,61,48,120,56,98,44,67,97,110,99,101,108,108,101,100,61,48,120,51,44,67,97,114,100,82,101,97,100,101,114,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,102,44,67,97,114,100,82,101,97,100,101,114,85,110,107,110,111,119,110,61,48,120,57,100,44,67,111,109,112,114,101,115,115,105,111,110,61,48,120,97,100,44,67,111,110,110,101,99,116,105,111,110,65,98,111,114,116,101,100,61,48,120,56,99,44,67,111,110,110,101,99,116,105,111,110,82,101,102,117,115,101,100,61,48,120,56,51,44,67,111,110,115,116,114,97,105,110,116,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,67,111,110,116,105,110,117,101,61,48,120,53,44,67,111,114,101,86,101,114,115,105,111,110,61,48,120,50,54,44,67,114,101,97,116,101,70,105,108,101,61,48,120,55,52,44,67,114,101,97,116,101,79,98,106,101,99,116,61,48,120,54,53,44,67,114,101,97,116,101,82,101,115,111,117,114,99,101,61,48,120,98,50,44,68,97,116,97,83,105,122,101,61,48,120,56,97,44,68,101,97,99,116,105,118,97,116,101,100,61,48,120,57,97,44,68,101,97,100,76,111,99,107,61,48,120,51,101,44,68,101,99,111,109,112,114,101,115,115,105,111,110,61,48,120,97,99,44,68,101,108,101,116,101,70,105,108,101,61,48,120,55,53,44,68,105,114,69,109,112,116,121,61,48,120,56,44,68,105,115,99,111,110,110,101,99,116,101,100,61,48,120,56,54,44,68,111,78,111,116,69,120,112,117,110,103,101,61,48,120,51,48,44,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,55,56,44,68,111,117,98,108,101,73,110,105,116,61,48,120,52,51,44,68,114,97,119,61,48,120,52,56,44,69,78,68,61,48,120,98,101,44,69,109,112,116,121,83,116,114,105,110,103,61,48,120,54,100,44,69,110,100,79,102,70,105,108,101,61,48,120,55,101,44,69,110,116,114,121,77,105,115,115,105,110,103,72,101,97,100,101,114,61,48,120,51,97,44,69,120,97,109,105,110,101,70,97,105,108,101,100,61,48,120,49,57,44,69,120,99,101,112,116,105,111,110,61,48,120,97,51,44,69,120,99,101,112,116,105,111,110,84,104,114,101,115,104,111,108,100,61,48,120,57,44,69,120,99,108,117,115,105,118,101,68,101,110,105,101,100,61,48,120,53,51,44,69,120,101,99,86,105,111,108,97,116,105,111,110,61,48,120,56,102,44,69,120,105,115,116,115,61,48,120,55,97,44,69,120,112,101,99,116,101,100,70,105,108,101,61,48,120,54,102,44,69,120,112,101,99,116,101,100,70,111,108,100,101,114,61,48,120,97,101,44,70,97,105,108,101,100,61,48,120,100,44,70,97,108,115,101,61,48,120,49,44,70,105,101,108,100,78,111,116,83,101,116,61,48,120,52,52,44,70,105,101,108,100,83,101,97,114,99,104,61,48,120,51,50,44,70,105,101,108,100,84,121,112,101,77,105,115,109,97,116,99,104,61,48,120,53,97,44,70,105,108,101,61,48,120,101,44,70,105,108,101,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,49,50,44,70,105,108,101,69,120,105,115,116,115,61,48,120,54,51,44,70,105,108,101,78,111,116,70,111,117,110,100,61,48,120,49,50,44,70,105,108,101,82,101,97,100,70,108,97,103,61,48,120,52,54,44,70,105,108,101,87,114,105,116,101,70,108,97,103,61,48,120,52,55,44,70,105,110,105,115,104,101,100,61,48,120,55,101,44,70,117,110,99,116,105,111,110,61,48,120,98,53,44,71,101,116,70,105,101,108,100,61,48,120,53,54,44,71,101,116,83,117,114,102,97,99,101,73,110,102,111,61,48,120,55,100,44,72,111,115,116,78,111,116,70,111,117,110,100,61,48,120,56,49,44,72,111,115,116,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,53,44,73,100,101,110,116,105,99,97,108,80,97,116,104,115,61,48,120,55,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,65,116,116,101,109,112,116,61,48,120,51,57,44,73,108,108,101,103,97,108,65,99,116,105,111,110,73,68,61,48,120,51,55,44,73,108,108,101,103,97,108,65,100,100,114,101,115,115,61,48,120,57,49,44,73,108,108,101,103,97,108,77,101,116,104,111,100,73,68,61,48,120,51,54,44,73,109,109,117,116,97,98,108,101,61,48,120,97,102,44,73,110,85,115,101,61,48,120,99,44,73,110,105,116,61,48,120,50,49,44,73,110,105,116,77,111,100,117,108,101,61,48,120,49,49,44,73,110,112,117,116,79,117,116,112,117,116,61,48,120,57,52,44,73,110,116,101,103,114,105,116,121,86,105,111,108,97,116,105,111,110,61,48,120,56,56,44,73,110,118,97,108,105,100,68,97,116,97,61,48,120,102,44,73,110,118,97,108,105,100,68,105,109,101,110,115,105,111,110,61,48,120,53,57,44,73,110,118,97,108,105,100,72,84,84,80,82,101,115,112,111,110,115,101,61,48,120,97,49,44,73,110,118,97,108,105,100,72,97,110,100,108,101,61,48,120,57,54,44,73,110,118,97,108,105,100,79,98,106,101,99,116,61,48,120,56,101,44,73,110,118,97,108,105,100,80,97,116,104,61,48,120,51,51,44,73,110,118,97,108,105,100,82,101,102,101,114,101,110,99,101,61,48,120,97,50,44,73,110,118,97,108,105,100,83,116,97,116,101,61,48,120,56,48,44,73,110,118,97,108,105,100,85,82,73,61,48,120,56,50,44,73,110,118,97,108,105,100,86,97,108,117,101,61,48,120,57,56,44,76,105,109,105,116,101,100,83,117,99,99,101,115,115,61,48,120,50,44,76,105,115,116,67,104,105,108,100,114,101,110,61,48,120,54,97,44,76,111,97,100,77,111,100,117,108,101,61,48,120,57,53,44,76,111,99,107,61,48,120,49,56,44,76,111,99,107,70,97,105,108,101,100,61,48,120,49,56,44,76,111,99,107,77,117,116,101,120,61,48,120,97,97,44,76,111,99,107,82,101,113,117,105,114,101,100,61,48,120,57,98,44,76,111,99,107,101,100,61,48,120,57,99,44,76,111,111,112,61,48,120,54,50,44,76,111,115,116,67,108,97,115,115,61,48,120,49,97,44,76,111,115,116,79,119,110,101,114,61,48,120,50,102,44,76,111,119,67,97,112,97,99,105,116,121,61,48,120,50,48,44,77,97,114,107,101,100,70,111,114,68,101,108,101,116,105,111,110,61,48,120,51,53,44,77,101,109,111,114,121,61,48,120,49,100,44,77,101,109,111,114,121,67,111,114,114,117,112,116,61,48,120,51,49,44,77,101,109,111,114,121,68,111,101,115,78,111,116,69,120,105,115,116,61,48,120,51,100,44,77,101,109,111,114,121,73,110,102,111,61,48,120,54,54,44,77,105,115,109,97,116,99,104,61,48,120,53,101,44,77,105,115,115,105,110,103,67,108,97,115,115,61,48,120,52,53,44,77,105,115,115,105,110,103,67,108,97,115,115,78,97,109,101,61,48,120,50,97,44,77,105,115,115,105,110,103,80,97,116,104,61,48,120,52,99,44,77,111,100,117,108,101,73,110,105,116,70,97,105,108,101,100,61,48,120,51,99,44,77,111,100,117,108,101,77,105,115,115,105,110,103,73,110,105,116,61,48,120,51,98,44,77,111,100,117,108,101,77,105,115,115,105,110,103,78,97,109,101,61,48,120,52,48,44,77,111,100,117,108,101,79,112,101,110,70,97,105,108,101,100,61,48,120,51,56,44,78,101,101,100,79,119,110,101,114,61,48,120,50,52,44,78,101,101,100,87,105,100,116,104,72,101,105,103,104,116,61,48,120,50,55,44,78,101,103,97,116,105,118,101,67,108,97,115,115,73,68,61,48,120,50,57,44,78,101,103,97,116,105,118,101,83,117,98,67,108,97,115,115,73,68,61,48,120,50,56,44,78,101,116,119,111,114,107,85,110,114,101,97,99,104,97,98,108,101,61,48,120,56,52,44,78,101,119,79,98,106,101,99,116,61,48,120,53,53,44,78,111,65,99,116,105,111,110,61,48,120,49,98,44,78,111,68,97,116,97,61,48,120,49,53,44,78,111,70,105,101,108,100,65,99,99,101,115,115,61,48,120,53,55,44,78,111,77,97,116,99,104,105,110,103,79,98,106,101,99,116,61,48,120,52,97,44,78,111,77,101,100,105,97,73,110,115,101,114,116,101,100,61,48,120,57,101,44,78,111,77,101,109,111,114,121,61,48,120,97,44,78,111,77,101,116,104,111,100,115,61,48,120,52,57,44,78,111,80,101,114,109,105,115,115,105,111,110,61,48,120,50,50,44,78,111,80,111,105,110,116,101,114,61,48,120,98,44,78,111,83,101,97,114,99,104,82,101,115,117,108,116,61,48,120,52,101,44,78,111,83,116,97,116,115,61,48,120,49,102,44,78,111,83,117,112,112,111,114,116,61,48,120,49,99,44,78,111,116,70,111,117,110,100,61,48,120,49,48,44,78,111,116,73,110,105,116,105,97,108,105,115,101,100,61,48,120,54,55,44,78,111,116,76,111,99,107,101,100,61,48,120,52,100,44,78,111,116,80,111,115,115,105,98,108,101,61,48,120,98,51,44,78,111,116,104,105,110,103,68,111,110,101,61,48,120,52,44,78,111,116,105,102,105,101,100,61,48,120,52,48,48,48,48,48,48,48,44,78,117,108,108,65,114,103,115,61,48,120,56,100,44,79,98,106,101,99,116,67,111,114,114,117,112,116,61,48,120,53,48,44,79,98,106,101,99,116,69,120,105,115,116,115,61,48,120,54,101,44,79,98,115,111,108,101,116,101,61,48,120,98,49,44,79,98,116,97,105,110,77,101,116,104,111,100,61,48,120,50,99,44,79,107,97,121,61,48,120,48,44,79,112,101,110,70,105,108,101,61,48,120,55,54,44,79,112,101,110,71,76,61,48,120,97,53,44,79,117,116,79,102,66,111,117,110,100,115,61,48,120,53,102,44,79,117,116,79,102,68,97,116,97,61,48,120,55,101,44,79,117,116,79,102,82,97,110,103,101,61,48,120,50,98,44,79,117,116,79,102,83,112,97,99,101,61,48,120,55,99,44,79,117,116,115,105,100,101,77,97,105,110,84,104,114,101,97,100,61,48,120,97,54,44,79,119,110,101,114,78,101,101,100,115,66,105,116,109,97,112,61,48,120,50,53,44,79,119,110,101,114,80,97,115,115,84,104,114,111,117,103,104,61,48,120,53,49,44,80,101,114,109,105,115,115,105,111,110,68,101,110,105,101,100,61,48,120,50,50,44,80,101,114,109,105,115,115,105,111,110,115,61,48,120,50,50,44,80,114,111,120,121,83,83,76,84,117,110,110,101,108,61,48,120,97,48,44,81,117,101,114,121,61,48,120,50,101,44,82,101,97,100,61,48,120,49,54,44,82,101,97,100,70,105,108,101,84,111,66,117,102,102,101,114,61,48,120,98,48,44,82,101,97,100,79,110,108,121,61,48,120,55,55,44,82,101,97,108,108,111,99,77,101,109,111,114,121,61,48,120,54,49,44,82,101,99,117,114,115,105,111,110,61,48,120,57,48,44,82,101,100,105,109,101,110,115,105,111,110,61,48,120,55,49,44,82,101,102,114,101,115,104,61,48,120,54,57,44,82,101,115,105,122,101,61,48,120,55,48,44,82,101,115,111,108,118,101,80,97,116,104,61,48,120,54,52,44,82,101,115,111,108,118,101,83,121,109,98,111,108,61,48,120,98,52,44,82,101,115,111,117,114,99,101,69,120,105,115,116,115,61,48,120,54,56,44,82,101,116,114,121,61,48,120,55,44,83,97,110,105,116,121,70,97,105,108,117,114,101,61,48,120,55,98,44,83,99,104,101,109,97,86,105,111,108,97,116,105,111,110,61,48,120,56,57,44,83,101,97,114,99,104,61,48,120,49,48,44,83,101,99,117,114,105,116,121,61,48,120,57,55,44,83,101,101,107,61,48,120,54,48,44,83,101,114,118,105,99,101,85,110,97,118,97,105,108,97,98,108,101,61,48,120,57,57,44,83,101,116,70,105,101,108,100,61,48,120,51,52,44,83,101,116,86,97,108,117,101,78,111,116,65,114,114,97,121,61,48,120,98,99,44,83,101,116,86,97,108,117,101,78,111,116,70,117,110,99,116,105,111,110,61,48,120,98,97,44,83,101,116,86,97,108,117,101,78,111,116,76,111,111,107,117,112,61,48,120,98,100,44,83,101,116,86,97,108,117,101,78,111,116,78,117,109,101,114,105,99,61,48,120,98,55,44,83,101,116,86,97,108,117,101,78,111,116,79,98,106,101,99,116,61,48,120,98,57,44,83,101,116,86,97,108,117,101,78,111,116,80,111,105,110,116,101,114,61,48,120,98,98,44,83,101,116,86,97,108,117,101,78,111,116,83,116,114,105,110,103,61,48,120,98,56,44,83,101,116,86,111,108,117,109,101,61,48,120,97,98,44,83,107,105,112,61,48,120,54,44,83,109,97,108,108,77,97,115,107,61,48,120,54,99,44,83,116,97,116,101,109,101,110,116,85,110,115,97,116,105,115,102,105,101,100,61,48,120,52,102,44,83,116,114,105,110,103,70,111,114,109,97,116,61,48,120,55,102,44,83,121,110,116,97,120,61,48,120,55,102,44,83,121,115,116,101,109,67,97,108,108,61,48,120,54,98,44,83,121,115,116,101,109,67,111,114,114,117,112,116,61,48,120,50,51,44,83,121,115,116,101,109,76,111,99,107,101,100,61,48,120,51,102,44,84,97,115,107,83,116,105,108,108,69,120,105,115,116,115,61,48,120,56,55,44,84,101,114,109,105,110,97,116,101,61,48,120,57,44,84,104,114,101,97,100,65,108,114,101,97,100,121,65,99,116,105,118,101,61,48,120,97,52,44,84,104,114,101,97,100,78,111,116,76,111,99,107,101,100,61,48,120,97,57,44,84,105,109,101,79,117,116,61,48,120,49,101,44,84,114,117,101,61,48,120,48,44,85,110,98,97,108,97,110,99,101,100,88,77,76,61,48,120,57,50,44,85,110,100,101,102,105,110,101,100,70,105,101,108,100,61,48,120,52,52,44,85,110,114,101,99,111,103,110,105,115,101,100,70,105,101,108,100,84,121,112,101,61,48,120,53,98,44,85,110,115,117,112,112,111,114,116,101,100,70,105,101,108,100,61,48,120,53,100,44,85,110,115,117,112,112,111,114,116,101,100,79,119,110,101,114,61,48,120,53,50,44,85,115,101,83,117,98,67,108,97,115,115,61,48,120,97,55,44,86,105,114,116,117,97,108,86,111,108,117,109,101,61,48,120,53,56,44,87,111,117,108,100,66,108,111,99,107,61,48,120,57,51,44,87,114,105,116,101,61,48,120,49,55,44,87,114,111,110,103,67,108,97,115,115,61,48,120,56,101,44,87,114,111,110,103,79,98,106,101,99,116,84,121,112,101,61,48,120,56,101,44,87,114,111,110,103,84,121,112,101,61,48,120,97,56,44,87,114,111,110,103,86,101,114,115,105,111,110,61,48,120,49,51,10,99,46,69,86,71,58,65,78,68,82,79,73,68,61,48,120,100,44,65,80,80,61,48,120,99,44,65,85,68,73,79,61,48,120,56,44,67,76,65,83,83,61,48,120,98,44,68,73,83,80,76,65,89,61,48,120,53,44,69,78,68,61,48,120,101,44,70,73,76,69,83,89,83,84,69,77,61,48,120,49,44,71,85,73,61,48,120,52,44,72,65,82,68,87,65,82,69,61,48,120,55,44,73,79,61,48,120,54,44,78,69,84,87,79,82,75,61,48,120,50,44,80,79,87,69,82,61,48,120,97,44,83,89,83,84,69,77,61,48,120,51,44,85,83,69,82,61,48,120,57,10,99,46,70,66,75,58,67,79,80,89,95,70,73,76,69,61,48,120,50,44,68,69,76,69,84,69,95,70,73,76,69,61,48,120,51,44,77,79,86,69,95,70,73,76,69,61,48,120,49,10,99,46,70,68,58,65,76,76,79,67,61,48,120,50,48,44,65,82,82,65,89,61,48,120,49,48,48,48,44,65,82,82,65,89,83,73,90,69,61,48,120,56,48,44,66,85,70,70,69,82,61,48,120,50,48,48,44,66,85,70,83,73,90,69,61,48,120,56,48,44,66,89,84,69,61,48,120,49,48,48,48,48,48,48,44,67,80,80,61,48,120,52,48,48,48,44,67,85,83,84,79,77,61,48,120,56,48,48,48,44,68,79,85,66,76,69,61,48,120,56,48,48,48,48,48,48,48,44,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,48,48,48,48,49,48,48,44,69,82,82,79,82,61,48,120,56,48,48,44,70,76,65,71,83,61,48,120,52,48,44,70,76,79,65,84,61,48,120,49,48,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,61,48,120,50,48,48,48,48,48,48,44,70,85,78,67,84,73,79,78,80,84,82,61,48,120,97,48,48,48,48,48,48,44,73,61,48,120,52,48,48,44,73,78,73,84,61,48,120,52,48,48,44,76,65,82,71,69,61,48,120,52,48,48,48,48,48,48,44,76,65,82,71,69,82,69,83,85,76,84,61,48,120,52,48,48,48,49,48,48,44,76,79,67,65,76,61,48,120,50,44,76,79,78,71,61,48,120,52,48,48,48,48,48,48,48,44,76,79,78,71,82,69,83,85,76,84,61,48,120,52,48,48,48,48,49,48,48,44,76,79,79,75,85,80,61,48,120,56,48,44,79,66,74,69,67,84,61,48,120,49,44,79,66,74,69,67,84,73,68,61,48,120,52,48,48,48,48,48,48,49,44,79,66,74,69,67,84,80,84,82,61,48,120,56,48,48,48,48,48,49,44,80,79,73,78,84,69,82,61,48,120,56,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,49,48,48,48,48,44,80,84,82,61,48,120,56,48,48,48,48,48,48,44,80,84,82,66,85,70,70,69,82,61,48,120,56,48,48,48,50,48,48,44,80,84,82,82,69,83,85,76,84,61,48,120,56,48,48,48,49,48,48,44,80,84,82,83,73,90,69,61,48,120,56,48,44,80,84,82,95,68,79,85,66,76,69,82,69,83,85,76,84,61,48,120,56,56,48,48,48,49,48,48,44,80,84,82,95,76,65,82,71,69,82,69,83,85,76,84,61,48,120,99,48,48,48,49,48,48,44,80,84,82,95,76,79,78,71,82,69,83,85,76,84,61,48,120,52,56,48,48,48,49,48,48,44,82,61,48,120,49,48,48,44,82,69,65,68,61,48,120,49,48,48,44,82,69,83,79,85,82,67,69,61,48,120,50,48,48,48,44,82,69,83,85,76,84,61,48,120,49,48,48,44,82,71,66,61,48,120,56,48,48,48,48,44,82,73,61,48,120,53,48,48,44,82,87,61,48,120,51,48,48,44,83,67,65,76,69,68,61,48,120,50,48,48,48,48,48,44,83,84,82,61,48,120,56,48,48,48,48,48,44,83,84,82,73,78,71,61,48,120,56,48,48,48,48,48,44,83,84,82,82,69,83,85,76,84,61,48,120,56,48,48,49,48,48,44,83,84,82,85,67,84,61,48,120,49,48,44,83,89,78,79,78,89,77,61,48,120,50,48,48,48,48,44,83,89,83,84,69,77,61,48,120,49,48,48,48,48,44,84,65,71,83,61,48,120,52,48,48,44,85,78,73,84,61,48,120,50,48,48,48,48,48,48,48,44,85,78,83,73,71,78,69,68,61,48,120,52,48,48,48,48,44,86,65,82,84,65,71,83,61,48,120,52,48,44,86,73,82,84,85,65,76,61,48,120,56,44,86,79,73,68,61,48,120,48,44,86,79,76,65,84,73,76,69,61,48,120,48,44,87,61,48,120,50,48,48,44,87,79,82,68,61,48,120,52,48,48,48,48,48,44,87,82,73,84,69,61,48,120,50,48,48,10,99,46,70,68,66,58,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,50,44,68,69,67,79,77,80,82,69,83,83,95,70,73,76,69,61,48,120,49,44,68,69,67,79,77,80,82,69,83,83,95,79,66,74,69,67,84,61,48,120,52,44,82,69,77,79,86,69,95,70,73,76,69,61,48,120,51,10,99,46,70,68,76,58,70,69,69,68,66,65,67,75,61,48,120,49,10,99,46,70,68,84,58,65,67,67,69,83,83,69,68,61,48,120,50,44,65,82,67,72,73,86,69,68,61,48,120,51,44,67,82,69,65,84,69,68,61,48,120,49,44,77,79,68,73,70,73,69,68,61,48,120,48,10,99,46,70,70,82,58,65,66,79,82,84,61,48,120,50,44,67,79,78,84,73,78,85,69,61,48,120,48,44,79,75,65,89,61,48,120,48,44,83,75,73,80,61,48,120,49,10,99,46,70,76,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,49,48,44,66,85,70,70,69,82,61,48,120,52,48,44,68,69,86,73,67,69,61,48,120,52,48,48,44,68,73,82,69,67,84,79,82,89,61,48,120,56,44,69,88,67,76,85,68,69,95,70,73,76,69,83,61,48,120,49,48,48,48,44,69,88,67,76,85,68,69,95,70,79,76,68,69,82,83,61,48,120,50,48,48,48,44,70,73,76,69,61,48,120,49,48,48,44,70,79,76,68,69,82,61,48,120,56,44,76,73,78,75,61,48,120,50,48,44,76,79,79,80,61,48,120,56,48,44,78,69,87,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,83,69,84,95,68,65,84,69,61,48,120,50,48,48,44,83,84,82,69,65,77,61,48,120,56,48,48,44,87,82,73,84,69,61,48,120,49,10,99,46,70,79,70,58,83,77,65,82,84,95,78,65,77,69,83,61,48,120,49,10,99,46,73,68,84,89,80,69,58,70,85,78,67,84,73,79,78,61,48,120,51,44,71,76,79,66,65,76,61,48,120,50,44,77,69,83,83,65,71,69,61,48,120,49,10,99,46,74,69,84,58,65,66,83,95,88,89,61,48,120,49,98,44,65,78,65,76,79,71,50,95,88,89,61,48,120,49,54,44,65,78,65,76,79,71,50,95,90,61,48,120,49,55,44,65,78,65,76,79,71,95,88,89,61,48,120,49,52,44,65,78,65,76,79,71,95,90,61,48,120,49,53,44,66,85,84,84,79,78,95,49,61,48,120,50,44,66,85,84,84,79,78,95,49,48,61,48,120,98,44,66,85,84,84,79,78,95,50,61,48,120,51,44,66,85,84,84,79,78,95,51,61,48,120,52,44,66,85,84,84,79,78,95,52,61,48,120,53,44,66,85,84,84,79,78,95,53,61,48,120,54,44,66,85,84,84,79,78,95,54,61,48,120,55,44,66,85,84,84,79,78,95,55,61,48,120,56,44,66,85,84,84,79,78,95,56,61,48,120,57,44,66,85,84,84,79,78,95,57,61,48,120,97,44,66,85,84,84,79,78,95,83,69,76,69,67,84,61,48,120,102,44,66,85,84,84,79,78,95,83,84,65,82,84,61,48,120,101,44,67,82,79,83,83,69,68,95,73,78,61,48,120,49,99,44,67,82,79,83,83,69,68,95,79,85,84,61,48,120,49,100,44,68,69,86,73,67,69,95,84,73,76,84,95,88,89,61,48,120,49,102,44,68,69,86,73,67,69,95,84,73,76,84,95,90,61,48,120,50,48,44,68,73,71,73,84,65,76,95,88,89,61,48,120,49,44,68,73,83,80,76,65,89,95,69,68,71,69,61,48,120,50,49,44,69,78,68,61,48,120,50,50,44,76,69,70,84,95,66,85,77,80,69,82,95,49,61,48,120,49,48,44,76,69,70,84,95,66,85,77,80,69,82,95,50,61,48,120,49,49,44,76,77,66,61,48,120,50,44,77,77,66,61,48,120,52,44,80,69,78,95,84,73,76,84,95,88,89,61,48,120,49,97,44,80,82,69,83,83,85,82,69,61,48,120,49,101,44,82,73,71,72,84,95,66,85,77,80,69,82,95,49,61,48,120,49,50,44,82,73,71,72,84,95,66,85,77,80,69,82,95,50,61,48,120,49,51,44,82,77,66,61,48,120,51,44,84,82,73,71,71,69,82,95,76,69,70,84,61,48,120,99,44,84,82,73,71,71,69,82,95,82,73,71,72,84,61,48,120,100,44,87,72,69,69,76,61,48,120,49,56,44,87,72,69,69,76,95,84,73,76,84,61,48,120,49,57,10,99,46,74,84,89,80,69,58,65,78,65,76,79,71,61,48,120,50,48,44,65,78,67,72,79,82,69,68,61,48,120,50,44,66,85,84,84,79,78,61,48,120,56,48,44,67,82,79,83,83,73,78,71,61,48,120,56,44,68,66,76,95,67,76,73,67,75,61,48,120,50,48,48,44,68,73,71,73,84,65,76,61,48,120,49,48,44,68,82,65,71,71,69,68,61,48,120,52,44,68,82,65,71,95,73,84,69,77,61,48,120,56,48,48,44,69,88,84,95,77,79,86,69,77,69,78,84,61,48,120,52,48,44,77,79,86,69,77,69,78,84,61,48,120,49,48,48,44,82,69,80,69,65,84,69,68,61,48,120,52,48,48,44,83,69,67,79,78,68,65,82,89,61,48,120,49,10,99,46,75,69,89,58,65,61,48,120,49,44,65,80,79,83,84,82,79,80,72,69,61,48,120,50,98,44,65,84,61,48,120,56,97,44,66,61,48,120,50,44,66,65,67,75,61,48,120,56,54,44,66,65,67,75,83,80,65,67,69,61,48,120,54,57,44,66,65,67,75,95,83,76,65,83,72,61,48,120,50,102,44,66,82,69,65,75,61,48,120,55,98,44,67,61,48,120,51,44,67,65,76,76,61,48,120,56,55,44,67,65,77,69,82,65,61,48,120,56,57,44,67,65,78,67,69,76,61,48,120,55,97,44,67,65,80,83,95,76,79,67,75,61,48,120,52,54,44,67,76,69,65,82,61,48,120,54,101,44,67,79,77,77,65,61,48,120,50,99,44,68,61,48,120,52,44,68,69,76,69,84,69,61,48,120,54,100,44,68,79,84,61,48,120,50,100,44,68,79,87,78,61,48,120,54,49,44,69,61,48,120,53,44,69,73,71,72,84,61,48,120,50,50,44,69,78,68,61,48,120,55,50,44,69,78,68,95,67,65,76,76,61,48,120,56,56,44,69,78,84,69,82,61,48,120,54,98,44,69,81,85,65,76,83,61,48,120,50,55,44,69,83,67,65,80,69,61,48,120,54,99,44,69,88,69,67,85,84,69,61,48,120,55,52,44,70,61,48,120,54,44,70,49,61,48,120,52,99,44,70,49,48,61,48,120,53,53,44,70,49,49,61,48,120,53,54,44,70,49,50,61,48,120,53,55,44,70,49,51,61,48,120,53,56,44,70,49,52,61,48,120,53,57,44,70,49,53,61,48,120,53,97,44,70,49,54,61,48,120,53,98,44,70,49,55,61,48,120,53,99,44,70,49,56,61,48,120,56,48,44,70,49,57,61,48,120,56,49,44,70,50,61,48,120,52,100,44,70,50,48,61,48,120,56,50,44,70,51,61,48,120,52,101,44,70,52,61,48,120,52,102,44,70,53,61,48,120,53,48,44,70,54,61,48,120,53,49,44,70,55,61,48,120,53,50,44,70,56,61,48,120,53,51,44,70,57,61,48,120,53,52,44,70,73,78,68,61,48,120,55,57,44,70,73,86,69,61,48,120,49,102,44,70,79,82,87,65,82,68,61,48,120,57,48,44,70,79,85,82,61,48,120,49,101,44,71,61,48,120,55,44,72,61,48,120,56,44,72,69,76,80,61,48,120,52,51,44,72,79,77,69,61,48,120,54,102,44,73,61,48,120,57,44,73,78,83,69,82,84,61,48,120,55,53,44,74,61,48,120,97,44,75,61,48,120,98,44,76,61,48,120,99,44,76,69,70,84,61,48,120,54,51,44,76,69,78,83,95,70,79,67,85,83,61,48,120,56,99,44,76,69,83,83,95,71,82,69,65,84,69,82,61,48,120,53,102,44,76,73,83,84,95,69,78,68,61,48,120,57,54,44,76,95,65,76,84,61,48,120,52,56,44,76,95,67,79,77,77,65,78,68,61,48,120,52,97,44,76,95,67,79,78,84,82,79,76,61,48,120,52,49,44,76,95,83,72,73,70,84,61,48,120,52,52,44,76,95,83,81,85,65,82,69,61,48,120,50,56,44,77,61,48,120,100,44,77,65,67,82,79,61,48,120,53,100,44,77,69,78,85,61,48,120,55,56,44,77,73,78,85,83,61,48,120,50,54,44,77,85,84,69,61,48,120,57,50,44,78,61,48,120,101,44,78,69,88,84,61,48,120,56,101,44,78,73,78,69,61,48,120,50,51,44,78,80,95,48,61,48,120,51,49,44,78,80,95,49,61,48,120,51,50,44,78,80,95,50,61,48,120,51,51,44,78,80,95,51,61,48,120,51,52,44,78,80,95,52,61,48,120,51,53,44,78,80,95,53,61,48,120,51,54,44,78,80,95,54,61,48,120,51,55,44,78,80,95,55,61,48,120,51,56,44,78,80,95,56,61,48,120,51,57,44,78,80,95,57,61,48,120,51,97,44,78,80,95,66,65,82,61,48,120,51,100,44,78,80,95,68,69,67,73,77,65,76,61,48,120,51,102,44,78,80,95,68,73,86,73,68,69,61,48,120,52,48,44,78,80,95,68,79,84,61,48,120,51,102,44,78,80,95,69,78,84,69,82,61,48,120,55,101,44,78,80,95,77,73,78,85,83,61,48,120,51,101,44,78,80,95,77,85,76,84,73,80,76,89,61,48,120,51,98,44,78,80,95,80,76,85,83,61,48,120,51,99,44,78,80,95,80,76,85,83,95,77,73,78,85,83,61,48,120,53,101,44,78,80,95,83,69,80,65,82,65,84,79,82,61,48,120,51,100,44,78,85,77,95,76,79,67,75,61,48,120,55,99,44,79,61,48,120,102,44,79,78,69,61,48,120,49,98,44,80,61,48,120,49,48,44,80,65,71,69,95,68,79,87,78,61,48,120,55,49,44,80,65,71,69,95,85,80,61,48,120,55,48,44,80,65,85,83,69,61,48,120,54,53,44,80,69,82,73,79,68,61,48,120,50,100,44,80,76,65,89,61,48,120,57,53,44,80,76,85,83,61,48,120,56,98,44,80,79,85,78,68,61,48,120,57,52,44,80,79,87,69,82,61,48,120,54,56,44,80,82,69,86,73,79,85,83,61,48,120,56,102,44,80,82,73,78,84,61,48,120,52,55,44,80,82,84,95,83,67,82,61,48,120,55,100,44,81,61,48,120,49,49,44,82,61,48,120,49,50,44,82,69,68,79,61,48,120,55,55,44,82,69,86,69,82,83,69,95,81,85,79,84,69,61,48,120,50,53,44,82,69,87,73,78,68,61,48,120,57,49,44,82,73,71,72,84,61,48,120,54,50,44,82,95,65,76,84,61,48,120,52,57,44,82,95,67,79,77,77,65,78,68,61,48,120,52,98,44,82,95,67,79,78,84,82,79,76,61,48,120,52,50,44,82,95,83,72,73,70,84,61,48,120,52,53,44,82,95,83,81,85,65,82,69,61,48,120,50,57,44,83,61,48,120,49,51,44,83,67,82,95,76,79,67,75,61,48,120,54,52,44,83,69,76,69,67,84,61,48,120,55,51,44,83,69,77,73,95,67,79,76,79,78,61,48,120,50,97,44,83,69,86,69,78,61,48,120,50,49,44,83,73,88,61,48,120,50,48,44,83,76,65,83,72,61,48,120,50,101,44,83,76,69,69,80,61,48,120,54,55,44,83,80,65,67,69,61,48,120,51,48,44,83,84,65,82,61,48,120,57,51,44,83,84,79,80,61,48,120,56,100,44,83,89,83,82,81,61,48,120,55,102,44,84,61,48,120,49,52,44,84,65,66,61,48,120,54,97,44,84,72,82,69,69,61,48,120,49,100,44,84,87,79,61,48,120,49,99,44,85,61,48,120,49,53,44,85,78,68,79,61,48,120,55,54,44,85,80,61,48,120,54,48,44,86,61,48,120,49,54,44,86,79,76,85,77,69,95,68,79,87,78,61,48,120,56,53,44,86,79,76,85,77,69,95,85,80,61,48,120,56,52,44,87,61,48,120,49,55,44,87,65,75,69,61,48,120,54,54,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,56,51,44,88,61,48,120,49,56,44,89,61,48,120,49,57,44,90,61,48,120,49,97,44,90,69,82,79,61,48,120,50,52,10,99,46,75,81,58,65,76,84,61,48,120,54,48,44,65,76,84,71,82,61,48,120,52,48,44,67,65,80,83,95,76,79,67,75,61,48,120,52,44,67,79,77,77,65,78,68,61,48,120,49,56,48,44,67,79,78,84,82,79,76,61,48,120,49,56,44,67,84,82,76,61,48,120,49,56,44,68,69,65,68,95,75,69,89,61,48,120,49,48,48,48,48,44,73,78,70,79,61,48,120,51,99,48,52,44,73,78,83,84,82,85,67,84,73,79,78,95,75,69,89,83,61,48,120,55,56,44,76,95,65,76,84,61,48,120,50,48,44,76,95,67,79,77,77,65,78,68,61,48,120,56,48,44,76,95,67,79,78,84,82,79,76,61,48,120,56,44,76,95,67,84,82,76,61,48,120,56,44,76,95,83,72,73,70,84,61,48,120,49,44,78,79,84,95,80,82,73,78,84,65,66,76,69,61,48,120,50,48,48,48,44,78,85,77,95,76,79,67,75,61,48,120,56,48,48,48,44,78,85,77,95,80,65,68,61,48,120,50,48,48,44,80,82,69,83,83,69,68,61,48,120,49,48,48,48,44,81,85,65,76,73,70,73,69,82,83,61,48,120,49,102,98,44,82,69,76,69,65,83,69,68,61,48,120,56,48,48,44,82,69,80,69,65,84,61,48,120,52,48,48,44,82,95,65,76,84,61,48,120,52,48,44,82,95,67,79,77,77,65,78,68,61,48,120,49,48,48,44,82,95,67,79,78,84,82,79,76,61,48,120,49,48,44,82,95,67,84,82,76,61,48,120,49,48,44,82,95,83,72,73,70,84,61,48,120,50,44,83,67,82,95,76,79,67,75,61,48,120,52,48,48,48,44,83,72,73,70,84,61,48,120,51,44,87,73,78,95,67,79,78,84,82,79,76,61,48,120,50,48,48,48,48,10,99,46,76,65,89,79,85,84,58,66,65,67,75,71,82,79,85,78,68,61,48,120,56,44,69,77,66,69,68,68,69,68,61,48,120,50,48,44,70,79,82,69,71,82,79,85,78,68,61,48,120,49,48,44,73,71,78,79,82,69,95,67,85,82,83,79,82,61,48,120,56,48,44,76,69,70,84,61,48,120,50,44,76,79,67,75,61,48,120,52,48,44,82,73,71,72,84,61,48,120,52,44,83,81,85,65,82,69,61,48,120,48,44,84,73,71,72,84,61,48,120,49,44,84,73,76,69,61,48,120,49,48,48,44,87,73,68,69,61,48,120,54,10,99,46,76,68,70,58,67,72,69,67,75,95,69,88,73,83,84,83,61,48,120,49,10,99,46,76,79,67,58,68,73,82,69,67,84,79,82,89,61,48,120,49,44,70,73,76,69,61,48,120,51,44,70,79,76,68,69,82,61,48,120,49,44,86,79,76,85,77,69,61,48,120,50,10,99,46,77,65,88,58,70,73,76,69,78,65,77,69,61,48,120,49,48,48,44,78,65,77,69,95,76,69,78,61,48,120,49,102,10,99,46,77,69,77,58,65,85,68,73,79,61,48,120,56,44,67,65,76,76,69,82,61,48,120,56,48,48,48,48,48,44,67,79,68,69,61,48,120,49,48,44,67,79,76,76,69,67,84,61,48,120,49,48,48,48,44,68,65,84,65,61,48,120,48,44,69,88,67,76,85,83,73,86,69,61,48,120,56,48,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,48,48,44,77,65,78,65,71,69,68,61,48,120,49,44,78,79,95,66,76,79,67,75,61,48,120,50,48,48,48,44,78,79,95,66,76,79,67,75,73,78,71,61,48,120,50,48,48,48,44,78,79,95,67,76,69,65,82,61,48,120,52,48,48,48,48,44,78,79,95,76,79,67,75,61,48,120,52,48,48,44,78,79,95,80,79,79,76,61,48,120,50,48,44,79,66,74,69,67,84,61,48,120,50,48,48,44,82,69,65,68,61,48,120,49,48,48,48,48,44,82,69,65,68,95,87,82,73,84,69,61,48,120,51,48,48,48,48,44,83,84,82,73,78,71,61,48,120,49,48,48,44,84,69,88,84,85,82,69,61,48,120,52,44,84,77,80,95,76,79,67,75,61,48,120,52,48,44,85,78,84,82,65,67,75,69,68,61,48,120,56,48,44,86,73,68,69,79,61,48,120,50,44,87,82,73,84,69,61,48,120,50,48,48,48,48,10,99,46,77,70,70,58,65,84,84,82,73,66,61,48,120,50,48,44,67,76,79,83,69,68,61,48,120,56,48,44,67,82,69,65,84,69,61,48,120,52,44,68,69,69,80,61,48,120,49,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,70,73,76,69,61,48,120,52,48,48,44,70,79,76,68,69,82,61,48,120,50,48,48,44,77,79,68,73,70,89,61,48,120,50,44,77,79,86,69,68,61,48,120,49,48,44,79,80,69,78,69,68,61,48,120,52,48,44,82,69,65,68,61,48,120,49,44,82,69,78,65,77,69,61,48,120,49,48,44,83,69,76,70,61,48,120,56,48,48,44,85,78,77,79,85,78,84,61,48,120,49,48,48,44,87,82,73,84,69,61,48,120,50,10,99,46,77,72,70,58,68,69,70,65,85,76,84,61,48,120,50,44,83,84,65,84,73,67,61,48,120,49,44,83,84,82,85,67,84,85,82,69,61,48,120,50,10,99,46,77,79,70,58,76,73,78,75,95,76,73,66,82,65,82,89,61,48,120,49,44,83,84,65,84,73,67,61,48,120,50,44,83,89,83,84,69,77,95,80,82,79,66,69,61,48,120,52,10,99,46,77,79,86,69,58,65,76,76,61,48,120,102,44,68,79,87,78,61,48,120,49,44,76,69,70,84,61,48,120,52,44,82,73,71,72,84,61,48,120,56,44,85,80,61,48,120,50,10,99,46,77,83,70,58,65,68,68,61,48,120,56,44,65,68,68,82,69,83,83,61,48,120,49,48,44,77,69,83,83,65,71,69,95,73,68,61,48,120,50,48,44,78,79,95,68,85,80,76,73,67,65,84,69,61,48,120,52,44,85,80,68,65,84,69,61,48,120,50,44,87,65,73,84,61,48,120,49,10,99,46,77,83,71,73,68,58,65,67,84,73,79,78,61,48,120,54,51,44,66,82,69,65,75,61,48,120,54,52,44,67,79,77,77,65,78,68,61,48,120,54,53,44,67,79,82,69,95,69,78,68,61,48,120,54,52,44,68,69,66,85,71,61,48,120,53,102,44,69,86,69,78,84,61,48,120,53,101,44,70,82,69,69,61,48,120,54,50,44,81,85,73,84,61,48,120,51,101,56,44,84,72,82,69,65,68,95,65,67,84,73,79,78,61,48,120,53,98,44,84,72,82,69,65,68,95,67,65,76,76,66,65,67,75,61,48,120,53,99,44,86,65,76,73,68,65,84,69,95,80,82,79,67,69,83,83,61,48,120,53,100,44,87,65,73,84,95,70,79,82,95,79,66,74,69,67,84,83,61,48,120,53,97,10,99,46,77,84,70,58,65,78,73,77,61,48,120,56,44,82,69,76,65,84,73,86,69,61,48,120,49,48,44,88,61,48,120,49,44,89,61,48,120,50,44,90,61,48,120,52,10,99,46,78,69,84,77,83,71,58,69,78,68,61,48,120,49,44,83,84,65,82,84,61,48,120,48,10,99,46,78,70,58,67,79,76,76,69,67,84,61,48,120,56,48,44,70,82,69,69,61,48,120,49,48,44,70,82,69,69,95,79,78,95,85,78,76,79,67,75,61,48,120,56,44,73,78,73,84,73,65,76,73,83,69,68,61,48,120,50,44,76,79,67,65,76,61,48,120,52,44,77,69,83,83,65,71,69,61,48,120,50,48,48,44,78,65,77,69,61,48,120,56,48,48,48,48,48,48,48,44,80,82,73,86,65,84,69,61,48,120,48,44,82,69,67,76,65,83,83,69,68,61,48,120,49,48,48,44,83,73,71,78,65,76,76,69,68,61,48,120,52,48,48,44,83,85,80,80,82,69,83,83,95,76,79,71,61,48,120,52,48,44,84,73,77,69,82,95,83,85,66,61,48,120,50,48,44,85,78,73,81,85,69,61,48,120,52,48,48,48,48,48,48,48,44,85,78,84,82,65,67,75,69,68,61,48,120,49,10,99,46,79,80,70,58,65,82,71,83,61,48,120,52,48,44,68,69,84,65,73,76,61,48,120,52,44,69,82,82,79,82,61,48,120,56,48,44,77,65,88,95,68,69,80,84,72,61,48,120,50,44,77,79,68,85,76,69,95,80,65,84,72,61,48,120,52,48,48,44,79,80,84,73,79,78,83,61,48,120,49,44,80,82,73,86,73,76,69,71,69,68,61,48,120,49,48,48,44,82,79,79,84,95,80,65,84,72,61,48,120,56,48,48,44,83,67,65,78,95,77,79,68,85,76,69,83,61,48,120,49,48,48,48,44,83,72,79,87,95,69,82,82,79,82,83,61,48,120,50,48,44,83,72,79,87,95,73,79,61,48,120,49,48,44,83,72,79,87,95,77,69,77,79,82,89,61,48,120,56,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,48,48,10,99,46,80,69,82,77,73,84,58,65,76,76,95,68,69,76,69,84,69,61,48,120,56,56,56,44,65,76,76,95,69,88,69,67,61,48,120,52,52,52,44,65,76,76,95,82,69,65,68,61,48,120,49,49,49,44,65,76,76,95,87,82,73,84,69,61,48,120,50,50,50,44,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,69,76,69,84,69,61,48,120,56,44,69,86,69,82,89,79,78,69,95,65,67,67,69,83,83,61,48,120,102,102,102,44,69,86,69,82,89,79,78,69,95,68,69,76,69,84,69,61,48,120,56,56,56,44,69,86,69,82,89,79,78,69,95,69,88,69,67,61,48,120,52,52,52,44,69,86,69,82,89,79,78,69,95,82,69,65,68,61,48,120,49,49,49,44,69,86,69,82,89,79,78,69,95,82,69,65,68,87,82,73,84,69,61,48,120,51,51,51,44,69,86,69,82,89,79,78,69,95,87,82,73,84,69,61,48,120,50,50,50,44,69,88,69,67,61,48,120,52,44,71,82,79,85,80,61,48,120,102,48,44,71,82,79,85,80,73,68,61,48,120,49,48,48,48,48,44,71,82,79,85,80,95,68,69,76,69,84,69,61,48,120,56,48,44,71,82,79,85,80,95,69,88,69,67,61,48,120,52,48,44,71,82,79,85,80,95,82,69,65,68,61,48,120,49,48,44,71,82,79,85,80,95,87,82,73,84,69,61,48,120,50,48,44,72,73,68,68,69,78,61,48,120,49,48,48,48,44,73,78,72,69,82,73,84,61,48,120,50,48,48,48,48,44,78,69,84,87,79,82,75,61,48,120,56,48,48,48,48,44,79,70,70,76,73,78,69,61,48,120,52,48,48,48,48,44,79,84,72,69,82,83,61,48,120,102,48,48,44,79,84,72,69,82,83,95,68,69,76,69,84,69,61,48,120,56,48,48,44,79,84,72,69,82,83,95,69,88,69,67,61,48,120,52,48,48,44,79,84,72,69,82,83,95,82,69,65,68,61,48,120,49,48,48,44,79,84,72,69,82,83,95,87,82,73,84,69,61,48,120,50,48,48,44,80,65,83,83,87,79,82,68,61,48,120,52,48,48,48,44,82,69,65,68,61,48,120,49,44,85,83,69,82,61,48,120,102,44,85,83,69,82,73,68,61,48,120,56,48,48,48,44,85,83,69,82,95,69,88,69,67,61,48,120,52,44,85,83,69,82,95,82,69,65,68,61,48,120,49,44,85,83,69,82,95,87,82,73,84,69,61,48,120,50,44,87,82,73,84,69,61,48,120,50,10,99,46,80,77,70,58,83,89,83,84,69,77,95,78,79,95,66,82,69,65,75,61,48,120,49,10,99,46,80,84,67,58,67,82,79,83,83,72,65,73,82,61,48,120,97,44,67,85,83,84,79,77,61,48,120,49,55,44,68,69,70,65,85,76,84,61,48,120,49,44,68,82,65,71,71,65,66,76,69,61,48,120,49,56,44,69,78,68,61,48,120,49,57,44,72,65,78,68,61,48,120,49,48,44,72,65,78,68,95,76,69,70,84,61,48,120,49,49,44,72,65,78,68,95,82,73,71,72,84,61,48,120,49,50,44,73,78,86,73,83,73,66,76,69,61,48,120,49,54,44,77,65,71,78,73,70,73,69,82,61,48,120,102,44,78,79,95,67,72,65,78,71,69,61,48,120,48,44,80,65,73,78,84,66,82,85,83,72,61,48,120,49,52,44,83,73,90,69,95,66,79,84,84,79,77,61,48,120,57,44,83,73,90,69,95,66,79,84,84,79,77,95,76,69,70,84,61,48,120,50,44,83,73,90,69,95,66,79,84,84,79,77,95,82,73,71,72,84,61,48,120,51,44,83,73,90,69,95,76,69,70,84,61,48,120,54,44,83,73,90,69,95,82,73,71,72,84,61,48,120,55,44,83,73,90,69,95,84,79,80,61,48,120,56,44,83,73,90,69,95,84,79,80,95,76,69,70,84,61,48,120,52,44,83,73,90,69,95,84,79,80,95,82,73,71,72,84,61,48,120,53,44,83,73,90,73,78,71,61,48,120,99,44,83,76,69,69,80,61,48,120,98,44,83,80,76,73,84,95,72,79,82,73,90,79,78,84,65,76,61,48,120,101,44,83,80,76,73,84,95,86,69,82,84,73,67,65,76,61,48,120,100,44,83,84,79,80,61,48,120,49,53,44,84,69,88,84,61,48,120,49,51,10,99,46,82,68,70,58,65,82,67,72,73,86,69,61,48,120,50,48,48,48,44,68,65,84,69,61,48,120,50,44,70,73,76,69,61,48,120,56,44,70,73,76,69,83,61,48,120,56,44,70,79,76,68,69,82,61,48,120,49,48,44,70,79,76,68,69,82,83,61,48,120,49,48,44,72,73,68,68,69,78,61,48,120,49,48,48,44,76,73,78,75,61,48,120,52,48,44,79,80,69,78,68,73,82,61,48,120,52,48,48,48,44,80,69,82,77,73,83,83,73,79,78,83,61,48,120,52,44,81,85,65,76,73,70,73,69,68,61,48,120,50,48,48,44,81,85,65,76,73,70,89,61,48,120,50,48,48,44,82,69,65,68,95,65,76,76,61,48,120,49,102,44,82,69,65,68,95,79,78,76,89,61,48,120,49,48,48,48,44,83,73,90,69,61,48,120,49,44,83,84,82,69,65,77,61,48,120,56,48,48,44,84,65,71,83,61,48,120,56,48,44,84,73,77,69,61,48,120,50,44,86,73,82,84,85,65,76,61,48,120,52,48,48,44,86,79,76,85,77,69,61,48,120,50,48,10,99,46,82,69,83,58,67,79,78,83,79,76,69,95,70,68,61,48,120,50,44,67,79,82,69,95,73,68,76,61,48,120,56,44,67,80,85,95,83,80,69,69,68,61,48,120,49,54,44,68,73,83,80,76,65,89,95,68,82,73,86,69,82,61,48,120,53,44,69,88,67,69,80,84,73,79,78,95,72,65,78,68,76,69,82,61,48,120,49,49,44,70,82,69,69,95,77,69,77,79,82,89,61,48,120,49,55,44,70,82,69,69,95,83,87,65,80,61,48,120,49,44,74,78,73,95,69,78,86,61,48,120,101,44,75,69,89,95,83,84,65,84,69,61,48,120,51,44,76,79,71,95,68,69,80,84,72,61,48,120,100,44,76,79,71,95,76,69,86,69,76,61,48,120,97,44,77,65,88,95,80,82,79,67,69,83,83,69,83,61,48,120,99,44,78,69,84,95,80,82,79,67,69,83,83,73,78,71,61,48,120,49,50,44,79,80,69,78,95,73,78,70,79,61,48,120,49,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,55,44,80,82,73,86,73,76,69,71,69,68,95,85,83,69,82,61,48,120,54,44,80,82,79,67,69,83,83,95,83,84,65,84,69,61,48,120,49,51,44,83,84,65,84,73,67,95,66,85,73,76,68,61,48,120,57,44,84,72,82,69,65,68,95,73,68,61,48,120,102,44,84,79,84,65,76,95,77,69,77,79,82,89,61,48,120,49,52,44,84,79,84,65,76,95,83,72,65,82,69,68,95,77,69,77,79,82,89,61,48,120,98,44,84,79,84,65,76,95,83,87,65,80,61,48,120,49,53,44,85,83,69,82,95,73,68,61,48,120,52,10,99,46,82,70,68,58,65,76,76,79,87,95,82,69,67,85,82,83,73,79,78,61,48,120,50,48,44,65,76,87,65,89,83,95,67,65,76,76,61,48,120,49,48,48,44,69,88,67,69,80,84,61,48,120,50,44,82,69,65,68,61,48,120,52,44,82,69,67,65,76,76,61,48,120,56,48,44,82,69,77,79,86,69,61,48,120,56,44,83,79,67,75,69,84,61,48,120,52,48,44,83,84,79,80,95,82,69,67,85,82,83,69,61,48,120,49,48,44,87,82,73,84,69,61,48,120,49,10,99,46,82,80,58,77,79,68,85,76,69,95,80,65,84,72,61,48,120,49,44,82,79,79,84,95,80,65,84,72,61,48,120,51,44,83,89,83,84,69,77,95,80,65,84,72,61,48,120,50,10,99,46,82,83,70,58,65,80,80,82,79,88,73,77,65,84,69,61,48,120,52,44,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,50,48,44,67,72,69,67,75,95,86,73,82,84,85,65,76,61,48,120,50,44,78,79,95,68,69,69,80,95,83,67,65,78,61,48,120,56,44,78,79,95,70,73,76,69,95,67,72,69,67,75,61,48,120,49,44,80,65,84,72,61,48,120,49,48,10,99,46,83,67,70,58,69,88,73,84,95,79,78,95,69,82,82,79,82,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,10,99,46,83,69,69,75,58,67,85,82,82,69,78,84,61,48,120,49,44,69,78,68,61,48,120,50,44,82,69,76,65,84,73,86,69,61,48,120,51,44,83,84,65,82,84,61,48,120,48,10,99,46,83,84,82,58,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,67,65,83,69,61,48,120,49,44,77,65,84,67,72,95,76,69,78,61,48,120,50,10,99,46,83,84,84,58,70,76,79,65,84,61,48,120,50,44,72,69,88,61,48,120,51,44,78,85,77,66,69,82,61,48,120,49,44,83,84,82,73,78,71,61,48,120,52,10,99,46,84,72,70,58,65,85,84,79,95,70,82,69,69,61,48,120,49,10,99,46,84,79,73,58,65,78,68,82,79,73,68,95,65,83,83,69,84,77,71,82,61,48,120,52,44,65,78,68,82,79,73,68,95,67,76,65,83,83,61,48,120,51,44,65,78,68,82,79,73,68,95,69,78,86,61,48,120,50,44,76,79,67,65,76,95,67,65,67,72,69,61,48,120,48,44,76,79,67,65,76,95,83,84,79,82,65,71,69,61,48,120,49,10,99,46,84,83,70,58,65,84,84,65,67,72,69,68,61,48,120,49,48,48,44,68,69,84,65,67,72,69,68,61,48,120,56,48,44,70,79,82,69,73,71,78,61,48,120,49,44,76,79,71,95,65,76,76,61,48,120,50,48,44,80,73,80,69,61,48,120,50,48,48,44,80,82,73,86,73,76,69,71,69,68,61,48,120,56,44,81,85,73,69,84,61,48,120,52,48,44,82,69,83,69,84,95,80,65,84,72,61,48,120,52,44,83,72,69,76,76,61,48,120,49,48,44,87,65,73,84,61,48,120,50,10,99,46,84,83,84,65,84,69,58,80,65,85,83,69,68,61,48,120,49,44,82,85,78,78,73,78,71,61,48,120,48,44,83,84,79,80,80,73,78,71,61,48,120,50,44,84,69,82,77,73,78,65,84,69,68,61,48,120,51,10,99,46,86,65,83,58,67,65,83,69,95,83,69,78,83,73,84,73,86,69,61,48,120,102,44,67,76,79,83,69,95,68,73,82,61,48,120,54,44,67,82,69,65,84,69,95,76,73,78,75,61,48,120,49,49,44,68,69,76,69,84,69,61,48,120,51,44,68,69,82,69,71,73,83,84,69,82,61,48,120,49,44,68,82,73,86,69,82,95,83,73,90,69,61,48,120,49,50,44,71,69,84,95,68,69,86,73,67,69,95,73,78,70,79,61,48,120,98,44,71,69,84,95,73,78,70,79,61,48,120,97,44,73,68,69,78,84,73,70,89,95,70,73,76,69,61,48,120,99,44,73,71,78,79,82,69,95,70,73,76,69,61,48,120,57,44,77,65,75,69,95,68,73,82,61,48,120,100,44,79,80,69,78,95,68,73,82,61,48,120,53,44,82,69,65,68,95,76,73,78,75,61,48,120,49,48,44,82,69,78,65,77,69,61,48,120,52,44,83,65,77,69,95,70,73,76,69,61,48,120,101,44,83,67,65,78,95,68,73,82,61,48,120,50,44,84,69,83,84,95,80,65,84,72,61,48,120,55,44,87,65,84,67,72,95,80,65,84,72,61,48,120,56,10,99,46,86,76,70,58,65,80,73,61,48,120,50,48,44,66,82,65,78,67,72,61,48,120,49,44,67,82,73,84,73,67,65,76,61,48,120,56,44,68,69,84,65,73,76,61,48,120,52,48,44,69,82,82,79,82,61,48,120,50,44,70,85,78,67,84,73,79,78,61,48,120,49,48,48,44,73,78,70,79,61,48,120,49,48,44,84,82,65,67,69,61,48,120,56,48,44,87,65,82,78,73,78,71,61,48,120,52,10,99,46,86,79,76,85,77,69,58,72,73,68,68,69,78,61,48,120,52,44,80,82,73,79,82,73,84,89,61,48,120,50,44,82,69,80,76,65,67,69,61,48,120,49,44,83,89,83,84,69,77,61,48,120,56,10,0 }; diff --git a/src/core/lib_filesystem.cpp b/src/core/lib_filesystem.cpp index 1604dba67..b8e269681 100644 --- a/src/core/lib_filesystem.cpp +++ b/src/core/lib_filesystem.cpp @@ -1112,8 +1112,8 @@ ERR ReadFileToBuffer(CSTRING Path, APTR Buffer, LONG BufferSize, LONG *BytesRead -FUNCTION- ReadInfoTag: Read a named tag from a !FileInfo structure. -Call ReadInfoTag() to retrieve the string value associated with a named tag in a !FileInfo structure. The tag must -have been added with ~AddInfoTag() or `ERR::NotFound` will be returned. +ReadInfoTag() will read the value of a named tag in a !FileInfo structure. The tag must have been added with +~AddInfoTag() or `ERR::NotFound` will be returned. -INPUT- struct(FileInfo) Info: Pointer to a valid !FileInfo structure. @@ -2495,7 +2495,7 @@ ERR fs_getdeviceinfo(std::string_view Path, objStorageDevice *Info) if (glVolumes[vol].contains("Device")) { auto &device = glVolumes[vol]["Device"]; if (!device.compare("disk")) Info->DeviceFlags |= DEVICE::FLOPPY_DISK|DEVICE::REMOVABLE|DEVICE::READ|DEVICE::WRITE; - else if (!device.compare("fixed")) Info->DeviceFlags |= DEVICE::HARD_DISK|DEVICE::READ|DEVICE::WRITE; + else if (!device.compare("fixed")) Info->DeviceFlags |= DEVICE::HARD_DISK|DEVICE::READ|DEVICE::WRITE; else if (!device.compare("hd")) Info->DeviceFlags |= DEVICE::HARD_DISK|DEVICE::READ|DEVICE::WRITE; else if (!device.compare("cd")) Info->DeviceFlags |= DEVICE::COMPACT_DISC|DEVICE::REMOVABLE|DEVICE::READ; else if (!device.compare("usb")) Info->DeviceFlags |= DEVICE::USB|DEVICE::REMOVABLE; @@ -2509,12 +2509,12 @@ ERR fs_getdeviceinfo(std::string_view Path, objStorageDevice *Info) if (Info->DeviceFlags IS DEVICE::NIL) { // Unable to find a device reference for the volume, so try to resolve the path and try again. + Info->DeviceFlags |= DEVICE::BOOKMARK; if (!resolve.empty()) { // We've done what we can - drop through #ifdef _WIN32 // On win32 we can get the drive information from the drive letter // TODO: Write Win32 code to discover the drive type in GetDeviceInfo(). #endif - location.assign(resolve); } else { @@ -2539,7 +2539,7 @@ ERR fs_getdeviceinfo(std::string_view Path, objStorageDevice *Info) if (error IS ERR::Okay) { if (!(winGetFreeDiskSpace(location[0], &bytes_avail, &total_size))) { - log.msg("Failed to read location \"%s\" (from \"%s\")", location.c_str(), Path.data()); + log.msg("Failed to read disk space for \"%s\" (%s)", location.c_str(), Path.data()); Info->BytesFree = -1; Info->BytesUsed = 0; Info->DeviceSize = -1; diff --git a/src/core/microsoft/windows.cpp b/src/core/microsoft/windows.cpp index b1be37c38..86fb6c855 100644 --- a/src/core/microsoft/windows.cpp +++ b/src/core/microsoft/windows.cpp @@ -1400,16 +1400,16 @@ extern "C" ERR winCreateDir(const char *Path) //******************************************************************************************************************** // Returns TRUE on success. -extern "C" LONG winGetFreeDiskSpace(unsigned char Drive, long long *TotalSpace, long long *BytesUsed) +extern "C" LONG winGetFreeDiskSpace(char Drive, long long *TotalSpace, long long *BytesUsed) { DWORD sectors, bytes_per_sector, free_clusters, total_clusters; *TotalSpace = 0; *BytesUsed = 0; - std::string location = std::to_string(Drive) + ":\\"; + char location[4] = { Drive, ':', '\\', 0 }; - if (GetDiskFreeSpace(location.c_str(), §ors, &bytes_per_sector, &free_clusters, &total_clusters)) { + if (GetDiskFreeSpace(location, §ors, &bytes_per_sector, &free_clusters, &total_clusters)) { *TotalSpace = (double)sectors * (double)bytes_per_sector * (double)free_clusters; *BytesUsed = ((double)sectors * (double)bytes_per_sector * (double)total_clusters); return 1; From cf1945916305910c2197939dd72f8e9031f0469d Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 28 Nov 2024 21:05:40 +0000 Subject: [PATCH 16/17] [Scripts] Improved the presentation of toolbars in flush mode. --- scripts/gui/toolbar.fluid | 83 ++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/scripts/gui/toolbar.fluid b/scripts/gui/toolbar.fluid index 56523b56b..0928e3bc5 100644 --- a/scripts/gui/toolbar.fluid +++ b/scripts/gui/toolbar.fluid @@ -157,6 +157,7 @@ gui.toolbar = function(Options) local currentGroup = nil local spanCount = 0 while item do + -- Note that groups are converted to breaks when in flush mode. if item.type == TI_BREAK then item.x = x item.y = y @@ -174,46 +175,48 @@ gui.toolbar = function(Options) y = y + item.height end elseif item.type == TI_GROUP then - if self._horizontal then - if not item.prev then - x = x + groupOffset - else - x = x + (self._iconSize * 0.25) - end - else - if not item.prev then - y = y + groupOffset + if not Options.flush then + if self._horizontal then + if not item.prev then + x = x + groupOffset + else + x = x + (self._iconSize * 0.25) + end else - y = y + (self._iconSize * 0.25) + if not item.prev then + y = y + groupOffset + else + y = y + (self._iconSize * 0.25) + end end - end - currentGroup = item - spanCount = 0 + currentGroup = item + spanCount = 0 - item.x = x - item.y = y - if self._horizontal then - item.height = self._iconSize + (self._cellMargin * 2) - else - item.width = self._iconSize + (self._cellMargin * 2) - end - - item.bkgd = self.vg.new('VectorRectangle', { fill = gui.style.page.bkgd, - name = 'groupbkgd', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 - }) + item.x = x + item.y = y + if self._horizontal then + item.height = self._iconSize + (self._cellMargin * 2) + else + item.width = self._iconSize + (self._cellMargin * 2) + end - item.bkgd.acMoveToBack() + item.bkgd = self.vg.new('VectorRectangle', { fill = gui.style.page.bkgd, + name = 'groupbkgd', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 + }) - item.border = self.vg.new('VectorRectangle', { - name = 'grouprect', stroke = gui.style.button.bkgd, strokeWidth = gui.style.widget.strokeWidth, - x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 - }) + item.bkgd.acMoveToBack() - if self._groupClip then - item.clip = self._groupClip.viewport.new('VectorRectangle', { - name = 'groupclip', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 + item.border = self.vg.new('VectorRectangle', { + name = 'grouprect', stroke = gui.style.button.bkgd, strokeWidth = gui.style.widget.strokeWidth, + x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 }) + + if self._groupClip then + item.clip = self._groupClip.viewport.new('VectorRectangle', { + name = 'groupclip', x = x, y = y, roundX = self._round, roundY = self._round, width = 1, height = 1 + }) + end end elseif item.type == TI_ICON then item.x = x @@ -313,8 +316,10 @@ gui.toolbar = function(Options) -- Insert a visual break between items. self.addBreak = function() - newItem(TI_BREAK, 'break', nil) + if not self._first then return end -- At least one item must precede a break + local item = newItem(TI_BREAK, 'break', nil) self._arranged = false + return item end -- Add a clickable item to the toolbar. The Name acts as the item text that is displayed in the toolbar if no @@ -352,10 +357,14 @@ gui.toolbar = function(Options) -- known ahead of time, then added with calls to addItem(). self.addGroup = function(ID, Name, Span) - local item = newItem(TI_GROUP, Name, ID) - item.group = { span=Span } - self._groupCount = self._groupCount + 1 - return item + if Options.flush then + return self.addBreak() + else + local item = newItem(TI_GROUP, Name, ID) + item.group = { span=Span } + self._groupCount = self._groupCount + 1 + return item + end end -- Individual toolbar items can be disabled with this method. Reverse this action with enableItem(). From 832c257e3f7b0a6421a128c406c87e7831846a75 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Thu, 28 Nov 2024 22:32:53 +0000 Subject: [PATCH 17/17] Cleanup --- scripts/gui.fluid | 7 ++----- scripts/gui/columnview.fluid | 18 +++++++++--------- scripts/gui/listview.fluid | 17 ++--------------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 7e759189c..a813cb007 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -462,10 +462,9 @@ end icon = nil, -- Default icon theme for icons drawn over 'bkgd' textHighlight = gui.palette.altText, -- Colour for 'text' during user hover textHighlightBkgd = gui.palette.contrast, -- Background fill for 'text' during user hover - iconHighlight = nil, -- Recommended icon theme when highlighted + iconHighlight = nil, -- Recommended icon theme when highlighted or selected textSelect = gui.palette.altText, -- Colour for 'text' when selected - textSelectBkgd = gui.palette.prime, -- Background fill for 'text' when selected - iconSelect = nil -- Recommended icon theme when selected + textSelectBkgd = gui.palette.prime -- Background fill for 'text' when selected }, widget = { text = gui.palette.altText, @@ -511,7 +510,6 @@ end if gui.palette.type == 'light' then gui.style.page.icon = 'carbon' gui.style.page.iconHighlight = 'pearl' - gui.style.page.iconSelect = 'pearl' gui.style.page.text = gui.palette.text gui.style.page.textHighlight = gui.palette.altText gui.style.page.textSelect = gui.palette.altText @@ -521,7 +519,6 @@ end else gui.style.page.icon = 'pearl' gui.style.page.iconHighlight = 'carbon' - gui.style.page.iconSelect = 'carbon' gui.style.menu.icon = 'carbon' gui.style.dropdown.icon = 'pearl' gui.style.dropdown.iconHighlight = 'carbon' diff --git a/scripts/gui/columnview.fluid b/scripts/gui/columnview.fluid index c8c6fca61..f824a6d7a 100644 --- a/scripts/gui/columnview.fluid +++ b/scripts/gui/columnview.fluid @@ -241,13 +241,13 @@ local function bannerZoneClick(View, Col, X, Y, Value) if View._sort and (View._sort.col == Col) and (View._sort.ascending == true) then View._sort = { col=Col, ascending=false } if not View.iconDescending then - if not View.icons['sort_desc'] then - gui.createIcon(View.scene, 'layout/sort_alpha_desc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_desc') - View.icons['sort_desc'] = true + if not View._sort_desc then + local info = gui.createIcon(View.scene, 'layout/sort_alpha_desc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_desc') + View._sort_desc = info.fill end View._sort.icon = Col.bannerVP.new('VectorRectangle', { - fill = 'url(#sort_desc)', + fill = View._sort_desc, y = math.round( ((BANNER_HEIGHT - ICON_SIZE) * 0.5)), width = ICON_SIZE, height = ICON_SIZE @@ -256,13 +256,13 @@ local function bannerZoneClick(View, Col, X, Y, Value) else View._sort = { col=Col, ascending=true } if not View.iconAscending then - if not View.icons['sort_asc'] then - View.icons['sort_asc'] = true - gui.createIcon(View.scene, 'layout/sort_alpha_asc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_asc') + if not View._sort_asc then + local info = gui.createIcon(View.scene, 'layout/sort_alpha_asc', ICON_SIZE, gui.rgbToIconTheme(gui.style.widget.text), 'sort_asc') + View._sort_asc = info.fill end View._sort.icon = Col.bannerVP.new('VectorRectangle', { - fill = 'url(#sort_asc)', + fill = View._sort_asc, y = math.round((BANNER_HEIGHT - ICON_SIZE) * 0.5), width = ICON_SIZE, height = ICON_SIZE @@ -592,7 +592,7 @@ gui.columnView = function(Options) item._col[attrib].text.x = col.width - MARGIN else if col.showIcons then - item._col[attrib].text.x = (MARGIN * 2) + ICON_SIZE + MARGIN + item._col[attrib].text.x = (MARGIN * 2) + ICON_SIZE + (MARGIN * 2) else item._col[attrib].text.x = MARGIN end diff --git a/scripts/gui/listview.fluid b/scripts/gui/listview.fluid index 2ff679b4b..586a9505f 100644 --- a/scripts/gui/listview.fluid +++ b/scripts/gui/listview.fluid @@ -63,8 +63,8 @@ gui.listView = function(Options) setListTextFill(Item, gui.style.page.textSelect) Item._vectors.highlight.fill = gui.style.page.textSelectBkgd Item._vectors.highlight.visibility = VIS_VISIBLE - if gui.style.page.iconSelect and View.sIcons[Item.icon] then - Item._vectors.iconImage.fill = 'url(#' .. View.sIcons[Item.icon] .. ')' + if gui.style.page.iconHighlight and View.hIcons[Item.icon] then + Item._vectors.iconImage.fill = 'url(#' .. View.hIcons[Item.icon] .. ')' end elseif Highlighted then Item._vectors.highlight.fill = gui.style.page.textHighlightBkgd @@ -146,21 +146,9 @@ gui.listView = function(Options) View.icons[item.icon] = true end - if gui.style.page.iconSelect then - if gui.style.page.iconSelect == gui.style.page.icon then - View.sIcons[item.icon] = item.icon - else - gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconSelect, '_s_' .. item.icon) - View.sIcons[item.icon] = '_s_' .. item.icon - end - end - if gui.style.page.iconHighlight then if gui.style.page.iconHighlight == gui.style.page.icon then View.hIcons[item.icon] = item.icon - elseif gui.style.page.iconHighlight != gui.style.page.iconSelect then - gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconHighlight, '_s_' .. item.icon) - View.hIcons[item.icon] = '_s_' .. item.icon else gui.createIcon(View.scene, item.icon, ICON_SIZE, gui.style.page.iconHighlight, '_h_' .. item.icon) View.hIcons[item.icon] = '_h_' .. item.icon @@ -244,7 +232,6 @@ gui.listView = function(Options) local function constructListView(View, Scene, Window, Page) if not View.icons then View.icons = { } end - if not View.sIcons then View.sIcons = { } end if not View.hIcons then View.hIcons = { } end if #View.items > 0 then newItems(View, View.items) end