blur uses radius instead of intensity
This commit is contained in:
@@ -511,7 +511,7 @@ function flexlove.draw(gameDrawFunc, postDrawFunc)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
local function hasBackdropBlur(element)
|
local function hasBackdropBlur(element)
|
||||||
if element.backdropBlur and element.backdropBlur.intensity > 0 then
|
if element.backdropBlur and element.backdropBlur.radius > 0 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
for _, child in ipairs(element.children) do
|
for _, child in ipairs(element.children) do
|
||||||
|
|||||||
@@ -378,9 +378,10 @@ end</code></pre>
|
|||||||
<div class="note">
|
<div class="note">
|
||||||
<p>
|
<p>
|
||||||
<strong>💡 Tip:</strong> Use <code>positioning = "grid"</code> with
|
<strong>💡 Tip:</strong> Use <code>positioning = "grid"</code> with
|
||||||
<code>gridRows</code> and <code>gridColumns</code> to create true grid
|
<code>gridRows</code> and <code>gridColumns</code> to create true
|
||||||
layouts. Children auto-flow into cells left-to-right, top-to-bottom.
|
grid layouts. Children auto-flow into cells left-to-right,
|
||||||
Perfect for tables, schedules, and structured data displays.
|
top-to-bottom. Perfect for tables, schedules, and structured data
|
||||||
|
displays.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -589,7 +590,7 @@ local window = FlexLove.new({
|
|||||||
positioning = "flex",
|
positioning = "flex",
|
||||||
flexDirection = "vertical",
|
flexDirection = "vertical",
|
||||||
gap = 10,
|
gap = 10,
|
||||||
backdropBlur = { intensity = 50, quality = 10 },
|
backdropBlur = { radius = 10, quality = 10 },
|
||||||
backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8)
|
backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1088,28 +1089,28 @@ end</code></pre>
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add copy buttons to code blocks
|
// Add copy buttons to code blocks
|
||||||
document.querySelectorAll('pre code').forEach((codeBlock) => {
|
document.querySelectorAll("pre code").forEach((codeBlock) => {
|
||||||
const pre = codeBlock.parentElement;
|
const pre = codeBlock.parentElement;
|
||||||
const button = document.createElement('button');
|
const button = document.createElement("button");
|
||||||
button.className = 'copy-button';
|
button.className = "copy-button";
|
||||||
button.textContent = 'Copy';
|
button.textContent = "Copy";
|
||||||
button.title = 'Copy to clipboard';
|
button.title = "Copy to clipboard";
|
||||||
|
|
||||||
button.addEventListener('click', async () => {
|
button.addEventListener("click", async () => {
|
||||||
const code = codeBlock.textContent;
|
const code = codeBlock.textContent;
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(code);
|
await navigator.clipboard.writeText(code);
|
||||||
button.textContent = 'Copied!';
|
button.textContent = "Copied!";
|
||||||
button.classList.add('copied');
|
button.classList.add("copied");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
button.textContent = 'Copy';
|
button.textContent = "Copy";
|
||||||
button.classList.remove('copied');
|
button.classList.remove("copied");
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to copy:', err);
|
console.error("Failed to copy:", err);
|
||||||
button.textContent = 'Failed';
|
button.textContent = "Failed";
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
button.textContent = 'Copy';
|
button.textContent = "Copy";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function ScrollableContentExample.new()
|
|||||||
justifyContent = "center",
|
justifyContent = "center",
|
||||||
alignItems = "center",
|
alignItems = "center",
|
||||||
gap = 10,
|
gap = 10,
|
||||||
backdropBlur = { intensity = 50, quality = 10 },
|
backdropBlur = { radius = 20, quality = 10 },
|
||||||
backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8),
|
backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
119
modules/Blur.lua
119
modules/Blur.lua
@@ -12,7 +12,7 @@ local Cache = {
|
|||||||
MAX_CANVAS_SIZE = 20,
|
MAX_CANVAS_SIZE = 20,
|
||||||
MAX_QUAD_SIZE = 20,
|
MAX_QUAD_SIZE = 20,
|
||||||
MAX_BLURRED_CANVAS_CACHE = 50, -- Maximum cached blurred canvases
|
MAX_BLURRED_CANVAS_CACHE = 50, -- Maximum cached blurred canvases
|
||||||
INTENSITY_THRESHOLD = 5, -- Skip blur below this intensity
|
RADIUS_THRESHOLD = 0.5, -- Skip blur below this radius
|
||||||
LARGE_BLUR_THRESHOLD = 250 * 250, -- Warn if blur area exceeds this (250x250px)
|
LARGE_BLUR_THRESHOLD = 250 * 250, -- Warn if blur area exceeds this (250x250px)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,12 +133,12 @@ end
|
|||||||
---@param y number Y position
|
---@param y number Y position
|
||||||
---@param width number Width
|
---@param width number Width
|
||||||
---@param height number Height
|
---@param height number Height
|
||||||
---@param intensity number Blur intensity
|
---@param radius number Blur radius
|
||||||
---@param quality number Blur quality
|
---@param quality number Blur quality
|
||||||
---@param isBackdrop boolean Whether this is backdrop blur
|
---@param isBackdrop boolean Whether this is backdrop blur
|
||||||
---@return string key Cache key
|
---@return string key Cache key
|
||||||
function Cache.generateBlurCacheKey(elementId, x, y, width, height, intensity, quality, isBackdrop)
|
function Cache.generateBlurCacheKey(elementId, x, y, width, height, radius, quality, isBackdrop)
|
||||||
return string.format("%s:%d:%d:%d:%d:%d:%d:%s", elementId, x, y, width, height, intensity, quality, tostring(isBackdrop))
|
return string.format("%s:%d:%d:%d:%d:%.1f:%d:%s", elementId, x, y, width, height, radius, quality, tostring(isBackdrop))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get cached blurred canvas
|
--- Get cached blurred canvas
|
||||||
@@ -381,13 +381,13 @@ function Blur.new(props)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Apply blur to a region of the screen
|
--- Apply blur to a region of the screen
|
||||||
---@param intensity number Blur intensity (0-100)
|
---@param radius number Blur radius in pixels
|
||||||
---@param x number X position
|
---@param x number X position
|
||||||
---@param y number Y position
|
---@param y number Y position
|
||||||
---@param width number Width of region
|
---@param width number Width of region
|
||||||
---@param height number Height of region
|
---@param height number Height of region
|
||||||
---@param drawFunc function Function to draw content to be blurred
|
---@param drawFunc function Function to draw content to be blurred
|
||||||
function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
function Blur:applyToRegion(radius, x, y, width, height, drawFunc)
|
||||||
if type(drawFunc) ~= "function" then
|
if type(drawFunc) ~= "function" then
|
||||||
if Blur._ErrorHandler then
|
if Blur._ErrorHandler then
|
||||||
Blur._ErrorHandler:warn("Blur", "BLUR_001")
|
Blur._ErrorHandler:warn("Blur", "BLUR_001")
|
||||||
@@ -395,13 +395,13 @@ function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if intensity <= 0 or width <= 0 or height <= 0 then
|
if radius <= 0 or width <= 0 or height <= 0 then
|
||||||
drawFunc()
|
drawFunc()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Early exit for very low intensity (optimization)
|
-- Early exit for very low radius (optimization)
|
||||||
if intensity < Cache.INTENSITY_THRESHOLD then
|
if radius < Cache.RADIUS_THRESHOLD then
|
||||||
drawFunc()
|
drawFunc()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -409,11 +409,9 @@ function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
|||||||
-- Check for large blur area in immediate mode
|
-- Check for large blur area in immediate mode
|
||||||
checkLargeBlurWarning(nil, width, height, "content")
|
checkLargeBlurWarning(nil, width, height, "content")
|
||||||
|
|
||||||
intensity = math.max(0, math.min(100, intensity))
|
-- Calculate offset multiplier based on radius and quality
|
||||||
|
-- Higher quality = more samples = smaller steps for same radius
|
||||||
-- Intensity 0-100 maps to 0-5 passes
|
local offsetMultiplier = radius / self.quality
|
||||||
local passes = math.ceil(intensity / 20)
|
|
||||||
passes = math.max(1, math.min(5, passes))
|
|
||||||
|
|
||||||
local canvas1 = Cache.getCanvas(width, height)
|
local canvas1 = Cache.getCanvas(width, height)
|
||||||
local canvas2 = Cache.getCanvas(width, height)
|
local canvas2 = Cache.getCanvas(width, height)
|
||||||
@@ -435,17 +433,16 @@ function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
|||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.setBlendMode("alpha", "premultiplied")
|
love.graphics.setBlendMode("alpha", "premultiplied")
|
||||||
|
|
||||||
for i = 1, passes do
|
-- Single pass with radius-controlled offset
|
||||||
love.graphics.setCanvas(canvas2)
|
love.graphics.setCanvas(canvas2)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 1 / width, 0 })
|
self.shader:send("direction", { offsetMultiplier / width, 0 })
|
||||||
love.graphics.draw(canvas1, 0, 0)
|
love.graphics.draw(canvas1, 0, 0)
|
||||||
|
|
||||||
love.graphics.setCanvas(canvas1)
|
love.graphics.setCanvas(canvas1)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 0, 1 / height })
|
self.shader:send("direction", { 0, offsetMultiplier / height })
|
||||||
love.graphics.draw(canvas2, 0, 0)
|
love.graphics.draw(canvas2, 0, 0)
|
||||||
end
|
|
||||||
|
|
||||||
love.graphics.setCanvas(prevCanvas)
|
love.graphics.setCanvas(prevCanvas)
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
@@ -460,13 +457,13 @@ function Blur:applyToRegion(intensity, x, y, width, height, drawFunc)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Apply backdrop blur effect (blur content behind a region)
|
--- Apply backdrop blur effect (blur content behind a region)
|
||||||
---@param intensity number Blur intensity (0-100)
|
---@param radius number Blur radius in pixels
|
||||||
---@param x number X position
|
---@param x number X position
|
||||||
---@param y number Y position
|
---@param y number Y position
|
||||||
---@param width number Width of region
|
---@param width number Width of region
|
||||||
---@param height number Height of region
|
---@param height number Height of region
|
||||||
---@param backdropCanvas love.Canvas Canvas containing the backdrop content
|
---@param backdropCanvas love.Canvas Canvas containing the backdrop content
|
||||||
function Blur:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
|
function Blur:applyBackdrop(radius, x, y, width, height, backdropCanvas)
|
||||||
if not backdropCanvas then
|
if not backdropCanvas then
|
||||||
if Blur._ErrorHandler then
|
if Blur._ErrorHandler then
|
||||||
Blur._ErrorHandler:warn("Blur", "BLUR_002")
|
Blur._ErrorHandler:warn("Blur", "BLUR_002")
|
||||||
@@ -474,19 +471,17 @@ function Blur:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if intensity <= 0 or width <= 0 or height <= 0 then
|
if radius <= 0 or width <= 0 or height <= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Early exit for very low intensity (optimization)
|
-- Early exit for very low radius (optimization)
|
||||||
if intensity < Cache.INTENSITY_THRESHOLD then
|
if radius < Cache.RADIUS_THRESHOLD then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
intensity = math.max(0, math.min(100, intensity))
|
-- Calculate offset multiplier based on radius and quality
|
||||||
|
local offsetMultiplier = radius / self.quality
|
||||||
local passes = math.ceil(intensity / 20)
|
|
||||||
passes = math.max(1, math.min(5, passes))
|
|
||||||
|
|
||||||
local canvas1 = Cache.getCanvas(width, height)
|
local canvas1 = Cache.getCanvas(width, height)
|
||||||
local canvas2 = Cache.getCanvas(width, height)
|
local canvas2 = Cache.getCanvas(width, height)
|
||||||
@@ -507,17 +502,16 @@ function Blur:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
|
|||||||
|
|
||||||
love.graphics.setShader(self.shader)
|
love.graphics.setShader(self.shader)
|
||||||
|
|
||||||
for i = 1, passes do
|
-- Single pass with radius-controlled offset
|
||||||
love.graphics.setCanvas(canvas2)
|
love.graphics.setCanvas(canvas2)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 1 / width, 0 })
|
self.shader:send("direction", { offsetMultiplier / width, 0 })
|
||||||
love.graphics.draw(canvas1, 0, 0)
|
love.graphics.draw(canvas1, 0, 0)
|
||||||
|
|
||||||
love.graphics.setCanvas(canvas1)
|
love.graphics.setCanvas(canvas1)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 0, 1 / height })
|
self.shader:send("direction", { 0, offsetMultiplier / height })
|
||||||
love.graphics.draw(canvas2, 0, 0)
|
love.graphics.draw(canvas2, 0, 0)
|
||||||
end
|
|
||||||
|
|
||||||
love.graphics.setCanvas(prevCanvas)
|
love.graphics.setCanvas(prevCanvas)
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
@@ -550,21 +544,21 @@ function Blur.clearCache()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Apply backdrop blur with caching support
|
--- Apply backdrop blur with caching support
|
||||||
---@param intensity number Blur intensity (0-100)
|
---@param radius number Blur radius in pixels
|
||||||
---@param x number X position
|
---@param x number X position
|
||||||
---@param y number Y position
|
---@param y number Y position
|
||||||
---@param width number Width of region
|
---@param width number Width of region
|
||||||
---@param height number Height of region
|
---@param height number Height of region
|
||||||
---@param backdropCanvas love.Canvas Canvas containing the backdrop content
|
---@param backdropCanvas love.Canvas Canvas containing the backdrop content
|
||||||
---@param elementId string|nil Element ID for caching (nil disables caching)
|
---@param elementId string|nil Element ID for caching (nil disables caching)
|
||||||
function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas, elementId)
|
function Blur:applyBackdropCached(radius, x, y, width, height, backdropCanvas, elementId)
|
||||||
-- If caching is disabled or no element ID, fall back to regular apply
|
-- If caching is disabled or no element ID, fall back to regular apply
|
||||||
if not Blur._immediateModeOptimizations or not elementId then
|
if not Blur._immediateModeOptimizations or not elementId then
|
||||||
return self:applyBackdrop(intensity, x, y, width, height, backdropCanvas)
|
return self:applyBackdrop(radius, x, y, width, height, backdropCanvas)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Generate cache key
|
-- Generate cache key
|
||||||
local cacheKey = Cache.generateBlurCacheKey(elementId, x, y, width, height, intensity, self.quality, true)
|
local cacheKey = Cache.generateBlurCacheKey(elementId, x, y, width, height, radius, self.quality, true)
|
||||||
|
|
||||||
-- Check cache
|
-- Check cache
|
||||||
local cachedCanvas = Cache.getBlurredCanvas(cacheKey)
|
local cachedCanvas = Cache.getBlurredCanvas(cacheKey)
|
||||||
@@ -593,22 +587,20 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if intensity <= 0 or width <= 0 or height <= 0 then
|
if radius <= 0 or width <= 0 or height <= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Early exit for very low intensity (optimization)
|
-- Early exit for very low radius (optimization)
|
||||||
if intensity < Cache.INTENSITY_THRESHOLD then
|
if radius < Cache.RADIUS_THRESHOLD then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check for large blur area in immediate mode
|
-- Check for large blur area in immediate mode
|
||||||
checkLargeBlurWarning(elementId, width, height, "backdrop")
|
checkLargeBlurWarning(elementId, width, height, "backdrop")
|
||||||
|
|
||||||
intensity = math.max(0, math.min(100, intensity))
|
-- Calculate offset multiplier based on radius and quality
|
||||||
|
local offsetMultiplier = radius / self.quality
|
||||||
local passes = math.ceil(intensity / 20)
|
|
||||||
passes = math.max(1, math.min(5, passes))
|
|
||||||
|
|
||||||
local canvas1 = Cache.getCanvas(width, height)
|
local canvas1 = Cache.getCanvas(width, height)
|
||||||
local canvas2 = Cache.getCanvas(width, height)
|
local canvas2 = Cache.getCanvas(width, height)
|
||||||
@@ -629,17 +621,16 @@ function Blur:applyBackdropCached(intensity, x, y, width, height, backdropCanvas
|
|||||||
|
|
||||||
love.graphics.setShader(self.shader)
|
love.graphics.setShader(self.shader)
|
||||||
|
|
||||||
for i = 1, passes do
|
-- Single pass with radius-controlled offset
|
||||||
love.graphics.setCanvas(canvas2)
|
love.graphics.setCanvas(canvas2)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 1 / width, 0 })
|
self.shader:send("direction", { offsetMultiplier / width, 0 })
|
||||||
love.graphics.draw(canvas1, 0, 0)
|
love.graphics.draw(canvas1, 0, 0)
|
||||||
|
|
||||||
love.graphics.setCanvas(canvas1)
|
love.graphics.setCanvas(canvas1)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
self.shader:send("direction", { 0, 1 / height })
|
self.shader:send("direction", { 0, offsetMultiplier / height })
|
||||||
love.graphics.draw(canvas2, 0, 0)
|
love.graphics.draw(canvas2, 0, 0)
|
||||||
end
|
|
||||||
|
|
||||||
-- Cache the result
|
-- Cache the result
|
||||||
local cachedResult = love.graphics.newCanvas(width, height)
|
local cachedResult = love.graphics.newCanvas(width, height)
|
||||||
|
|||||||
@@ -70,8 +70,8 @@
|
|||||||
---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions
|
---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions
|
||||||
---@field scaleCorners number? -- Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)
|
---@field scaleCorners number? -- Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)
|
||||||
---@field scalingAlgorithm "nearest"|"bilinear"? -- Scaling algorithm for 9-patch corners: "nearest" (sharp/pixelated) or "bilinear" (smooth) (overrides theme setting)
|
---@field scalingAlgorithm "nearest"|"bilinear"? -- Scaling algorithm for 9-patch corners: "nearest" (sharp/pixelated) or "bilinear" (smooth) (overrides theme setting)
|
||||||
---@field contentBlur {intensity:number, quality:number}? -- Blur the element's content including children (intensity: 0-100, quality: 1-10)
|
---@field contentBlur {radius:number, quality:number?}? -- Blur the element's content including children (radius: pixels, quality: 1-10, default: 5)
|
||||||
---@field backdropBlur {intensity:number, quality:number}? -- Blur content behind the element (intensity: 0-100, quality: 1-10)
|
---@field backdropBlur {radius:number, quality:number?}? -- Blur content behind the element (radius: pixels, quality: 1-10, default: 5)
|
||||||
---@field _blurInstance table? -- Internal: cached blur effect instance
|
---@field _blurInstance table? -- Internal: cached blur effect instance
|
||||||
---@field editable boolean -- Whether the element is editable (default: false)
|
---@field editable boolean -- Whether the element is editable (default: false)
|
||||||
---@field multiline boolean -- Whether the element supports multiple lines (default: false)
|
---@field multiline boolean -- Whether the element supports multiple lines (default: false)
|
||||||
@@ -2123,10 +2123,10 @@ function Element:draw(backdropCanvas)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Apply content blur if configured
|
-- Apply content blur if configured
|
||||||
if self.contentBlur and self.contentBlur.intensity > 0 and #sortedChildren > 0 then
|
if self.contentBlur and self.contentBlur.radius > 0 and #sortedChildren > 0 then
|
||||||
local blurInstance = self:getBlurInstance()
|
local blurInstance = self:getBlurInstance()
|
||||||
if blurInstance then
|
if blurInstance then
|
||||||
Element._Blur.applyToRegion(blurInstance, self.contentBlur.intensity, self.x, self.y, borderBoxWidth, borderBoxHeight, drawChildren)
|
Element._Blur.applyToRegion(blurInstance, self.contentBlur.radius, self.x, self.y, borderBoxWidth, borderBoxHeight, drawChildren)
|
||||||
else
|
else
|
||||||
drawChildren()
|
drawChildren()
|
||||||
end
|
end
|
||||||
@@ -3190,13 +3190,13 @@ function Element:saveState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.backdropBlur then
|
if self.backdropBlur then
|
||||||
state.blur._backdropBlurIntensity = self.backdropBlur.intensity
|
state.blur._backdropBlurRadius = self.backdropBlur.radius
|
||||||
state.blur._backdropBlurQuality = self.backdropBlur.quality
|
state.blur._backdropBlurQuality = self.backdropBlur.quality or 5
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.contentBlur then
|
if self.contentBlur then
|
||||||
state.blur._contentBlurIntensity = self.contentBlur.intensity
|
state.blur._contentBlurRadius = self.contentBlur.radius
|
||||||
state.blur._contentBlurQuality = self.contentBlur.quality
|
state.blur._contentBlurQuality = self.contentBlur.quality or 5
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3252,9 +3252,9 @@ function Element:shouldInvalidateBlurCache(oldState, newState)
|
|||||||
or old._blurY ~= new._blurY
|
or old._blurY ~= new._blurY
|
||||||
or old._blurWidth ~= new._blurWidth
|
or old._blurWidth ~= new._blurWidth
|
||||||
or old._blurHeight ~= new._blurHeight
|
or old._blurHeight ~= new._blurHeight
|
||||||
or old._backdropBlurIntensity ~= new._backdropBlurIntensity
|
or old._backdropBlurRadius ~= new._backdropBlurRadius
|
||||||
or old._backdropBlurQuality ~= new._backdropBlurQuality
|
or old._backdropBlurQuality ~= new._backdropBlurQuality
|
||||||
or old._contentBlurIntensity ~= new._contentBlurIntensity
|
or old._contentBlurRadius ~= new._contentBlurRadius
|
||||||
or old._contentBlurQuality ~= new._contentBlurQuality
|
or old._contentBlurQuality ~= new._contentBlurQuality
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -417,12 +417,12 @@ function Renderer:draw(element, backdropCanvas)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- LAYER 0.5: Draw backdrop blur if configured (before background)
|
-- LAYER 0.5: Draw backdrop blur if configured (before background)
|
||||||
if self.backdropBlur and self.backdropBlur.intensity > 0 and backdropCanvas then
|
if self.backdropBlur and self.backdropBlur.radius > 0 and backdropCanvas then
|
||||||
local blurInstance = self:getBlurInstance()
|
local blurInstance = self:getBlurInstance()
|
||||||
if blurInstance then
|
if blurInstance then
|
||||||
-- Use cached blur in immediate mode if element has an ID
|
-- Use cached blur in immediate mode if element has an ID
|
||||||
local elementId = element.id and element.id ~= "" and element.id or nil
|
local elementId = element.id and element.id ~= "" and element.id or nil
|
||||||
blurInstance:applyBackdropCached(self.backdropBlur.intensity, element.x, element.y, borderBoxWidth, borderBoxHeight, backdropCanvas, elementId)
|
blurInstance:applyBackdropCached(self.backdropBlur.radius, element.x, element.y, borderBoxWidth, borderBoxHeight, backdropCanvas, elementId)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ local AnimationProps = {}
|
|||||||
---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions (default: sourced from theme or {1, 1})
|
---@field contentAutoSizingMultiplier {width:number?, height:number?}? -- Multiplier for auto-sized content dimensions (default: sourced from theme or {1, 1})
|
||||||
---@field scaleCorners number? -- Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)
|
---@field scaleCorners number? -- Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)
|
||||||
---@field scalingAlgorithm "nearest"|"bilinear"? -- Scaling algorithm for 9-patch corners: "nearest" (sharp/pixelated) or "bilinear" (smooth) (overrides theme setting)
|
---@field scalingAlgorithm "nearest"|"bilinear"? -- Scaling algorithm for 9-patch corners: "nearest" (sharp/pixelated) or "bilinear" (smooth) (overrides theme setting)
|
||||||
---@field contentBlur {intensity:number, quality:number}? -- Blur the element's content including children (intensity: 0-100, quality: 1-10, default: nil)
|
---@field contentBlur {radius:number, quality:number?}? -- Blur the element's content including children (radius: pixels, quality: 1-10, default(quality): 5)
|
||||||
---@field backdropBlur {intensity:number, quality:number}? -- Blur content behind the element (intensity: 0-100, quality: 1-10, default: nil)
|
---@field backdropBlur {radius:number, quality:number?}? -- Blur content behind the element (radius: pixels, quality: 1-10, default(quality): 5)
|
||||||
---@field editable boolean? -- Whether the element is editable (default: false)
|
---@field editable boolean? -- Whether the element is editable (default: false)
|
||||||
---@field multiline boolean? -- Whether the element supports multiple lines (default: false)
|
---@field multiline boolean? -- Whether the element supports multiple lines (default: false)
|
||||||
---@field textWrap boolean|"word"|"char"? -- Text wrapping mode (default: false for single-line, "word" for multi-line)
|
---@field textWrap boolean|"word"|"char"? -- Text wrapping mode (default: false for single-line, "word" for multi-line)
|
||||||
@@ -204,7 +204,7 @@ local FlexLoveConfig = {}
|
|||||||
---@field _blurY number
|
---@field _blurY number
|
||||||
---@field _blurWidth number
|
---@field _blurWidth number
|
||||||
---@field _blurHeight number
|
---@field _blurHeight number
|
||||||
---@field _backdropBlurIntensity number?
|
---@field _backdropBlurRadius number?
|
||||||
---@field _backdropBlurQuality string?
|
---@field _backdropBlurQuality number?
|
||||||
---@field _contentBlurIntensity number?
|
---@field _contentBlurRadius number?
|
||||||
---@field _contentBlurQuality string?
|
---@field _contentBlurQuality number?
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ end
|
|||||||
-- applyToRegion() Edge Cases
|
-- applyToRegion() Edge Cases
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
|
||||||
function TestBlur:testApplyToRegionWithZeroIntensity()
|
function TestBlur:testApplyToRegionWithZeroRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local called = false
|
local called = false
|
||||||
local drawFunc = function()
|
local drawFunc = function()
|
||||||
@@ -116,7 +116,7 @@ function TestBlur:testApplyToRegionWithZeroIntensity()
|
|||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestBlur:testApplyToRegionWithNegativeIntensity()
|
function TestBlur:testApplyToRegionWithNegativeRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local called = false
|
local called = false
|
||||||
local drawFunc = function()
|
local drawFunc = function()
|
||||||
@@ -176,14 +176,14 @@ function TestBlur:testApplyToRegionWithNegativeHeight()
|
|||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestBlur:testApplyToRegionWithIntensityOver100()
|
function TestBlur:testApplyToRegionWithLargeRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local called = false
|
local called = false
|
||||||
local drawFunc = function()
|
local drawFunc = function()
|
||||||
called = true
|
called = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Should clamp intensity to 100
|
-- Should work with large radius values
|
||||||
blur:applyToRegion(150, 0, 0, 100, 100, drawFunc)
|
blur:applyToRegion(150, 0, 0, 100, 100, drawFunc)
|
||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
end
|
end
|
||||||
@@ -240,36 +240,36 @@ function TestBlur:testApplyToRegionWithVeryLargeDimensions()
|
|||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestBlur:testApplyToRegionIntensityBoundaries()
|
function TestBlur:testApplyToRegionRadiusValues()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local called = false
|
local called = false
|
||||||
local drawFunc = function()
|
local drawFunc = function()
|
||||||
called = true
|
called = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test boundary values that affect passes calculation
|
-- Test various radius values
|
||||||
-- intensity 20 = 1 pass
|
-- Small radius
|
||||||
|
blur:applyToRegion(5, 0, 0, 100, 100, drawFunc)
|
||||||
|
luaunit.assertTrue(called)
|
||||||
|
|
||||||
|
called = false
|
||||||
|
-- Medium radius
|
||||||
|
blur:applyToRegion(10, 0, 0, 100, 100, drawFunc)
|
||||||
|
luaunit.assertTrue(called)
|
||||||
|
|
||||||
|
called = false
|
||||||
|
-- Large radius
|
||||||
blur:applyToRegion(20, 0, 0, 100, 100, drawFunc)
|
blur:applyToRegion(20, 0, 0, 100, 100, drawFunc)
|
||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
|
|
||||||
called = false
|
called = false
|
||||||
-- intensity 40 = 2 passes
|
-- Very large radius
|
||||||
blur:applyToRegion(40, 0, 0, 100, 100, drawFunc)
|
blur:applyToRegion(50, 0, 0, 100, 100, drawFunc)
|
||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
|
|
||||||
called = false
|
called = false
|
||||||
-- intensity 60 = 3 passes
|
-- Fractional radius
|
||||||
blur:applyToRegion(60, 0, 0, 100, 100, drawFunc)
|
blur:applyToRegion(2.5, 0, 0, 100, 100, drawFunc)
|
||||||
luaunit.assertTrue(called)
|
|
||||||
|
|
||||||
called = false
|
|
||||||
-- intensity 80 = 4 passes
|
|
||||||
blur:applyToRegion(80, 0, 0, 100, 100, drawFunc)
|
|
||||||
luaunit.assertTrue(called)
|
|
||||||
|
|
||||||
called = false
|
|
||||||
-- intensity 100 = 5 passes
|
|
||||||
blur:applyToRegion(100, 0, 0, 100, 100, drawFunc)
|
|
||||||
luaunit.assertTrue(called)
|
luaunit.assertTrue(called)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ end
|
|||||||
-- applyBackdrop() Edge Cases
|
-- applyBackdrop() Edge Cases
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
|
||||||
function TestBlur:testApplyBackdropWithZeroIntensity()
|
function TestBlur:testApplyBackdropWithZeroRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local mockCanvas = {
|
local mockCanvas = {
|
||||||
getDimensions = function()
|
getDimensions = function()
|
||||||
@@ -290,7 +290,7 @@ function TestBlur:testApplyBackdropWithZeroIntensity()
|
|||||||
luaunit.assertTrue(true)
|
luaunit.assertTrue(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestBlur:testApplyBackdropWithNegativeIntensity()
|
function TestBlur:testApplyBackdropWithNegativeRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local mockCanvas = {
|
local mockCanvas = {
|
||||||
getDimensions = function()
|
getDimensions = function()
|
||||||
@@ -337,7 +337,7 @@ function TestBlur:testApplyBackdropWithNilCanvas()
|
|||||||
luaunit.assertTrue(true) -- Should reach here without crash
|
luaunit.assertTrue(true) -- Should reach here without crash
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestBlur:testApplyBackdropWithIntensityOver100()
|
function TestBlur:testApplyBackdropWithLargeRadius()
|
||||||
local blur = Blur.new({quality = 5})
|
local blur = Blur.new({quality = 5})
|
||||||
local mockCanvas = {
|
local mockCanvas = {
|
||||||
getDimensions = function()
|
getDimensions = function()
|
||||||
@@ -345,7 +345,7 @@ function TestBlur:testApplyBackdropWithIntensityOver100()
|
|||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Should clamp intensity to 100
|
-- Should work with large radius values
|
||||||
blur:applyBackdrop(200, 0, 0, 100, 100, mockCanvas)
|
blur:applyBackdrop(200, 0, 0, 100, 100, mockCanvas)
|
||||||
luaunit.assertTrue(true)
|
luaunit.assertTrue(true)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ function TestCriticalFailures:test_blur_invalid_quality()
|
|||||||
FlexLove.new({
|
FlexLove.new({
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = 50, quality = 0 },
|
contentBlur = { radius = 50, quality = 0 },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ function TestCriticalFailures:test_blur_invalid_quality()
|
|||||||
FlexLove.new({
|
FlexLove.new({
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = 50, quality = -5 },
|
contentBlur = { radius = 50, quality = -5 },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -1881,7 +1881,7 @@ end
|
|||||||
|
|
||||||
function TestElementBlur:test_getBlurInstance_with_blur()
|
function TestElementBlur:test_getBlurInstance_with_blur()
|
||||||
local element = createBasicElement({
|
local element = createBasicElement({
|
||||||
backdropBlur = { intensity = 50, quality = 5 },
|
backdropBlur = { radius = 50, quality = 5 },
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Blur instance should be created when backdropBlur is set
|
-- Blur instance should be created when backdropBlur is set
|
||||||
@@ -1981,7 +1981,7 @@ end
|
|||||||
|
|
||||||
function TestElementDraw:test_draw_with_blur()
|
function TestElementDraw:test_draw_with_blur()
|
||||||
local element = createBasicElement({
|
local element = createBasicElement({
|
||||||
backdropBlur = { intensity = 50, quality = 5 },
|
backdropBlur = { radius = 50, quality = 5 },
|
||||||
backgroundColor = Color.new(1, 1, 1, 0.5),
|
backgroundColor = Color.new(1, 1, 1, 0.5),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -2906,7 +2906,7 @@ function TestElementEdgeCases:test_invalid_blur_config()
|
|||||||
id = "blur",
|
id = "blur",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = -10, quality = 5 },
|
contentBlur = { radius = -10, quality = 5 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element)
|
luaunit.assertNotNil(element)
|
||||||
|
|
||||||
@@ -2915,7 +2915,7 @@ function TestElementEdgeCases:test_invalid_blur_config()
|
|||||||
id = "blur2",
|
id = "blur2",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
backdropBlur = { intensity = 150, quality = 5 },
|
backdropBlur = { radius = 150, quality = 5 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element)
|
luaunit.assertNotNil(element)
|
||||||
|
|
||||||
@@ -2924,7 +2924,7 @@ function TestElementEdgeCases:test_invalid_blur_config()
|
|||||||
id = "blur3",
|
id = "blur3",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = 50, quality = 0 },
|
contentBlur = { radius = 50, quality = 0 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element)
|
luaunit.assertNotNil(element)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -438,26 +438,26 @@ TestRendererBlur = {}
|
|||||||
function TestRendererBlur:testNewWithContentBlur()
|
function TestRendererBlur:testNewWithContentBlur()
|
||||||
local renderer = Renderer.new({
|
local renderer = Renderer.new({
|
||||||
contentBlur = {
|
contentBlur = {
|
||||||
intensity = 5,
|
radius = 5,
|
||||||
quality = "high",
|
quality = "high",
|
||||||
},
|
},
|
||||||
}, createDeps())
|
}, createDeps())
|
||||||
|
|
||||||
luaunit.assertNotNil(renderer.contentBlur)
|
luaunit.assertNotNil(renderer.contentBlur)
|
||||||
luaunit.assertEquals(renderer.contentBlur.intensity, 5)
|
luaunit.assertEquals(renderer.contentBlur.radius, 5)
|
||||||
luaunit.assertEquals(renderer.contentBlur.quality, "high")
|
luaunit.assertEquals(renderer.contentBlur.quality, "high")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestRendererBlur:testNewWithBackdropBlur()
|
function TestRendererBlur:testNewWithBackdropBlur()
|
||||||
local renderer = Renderer.new({
|
local renderer = Renderer.new({
|
||||||
backdropBlur = {
|
backdropBlur = {
|
||||||
intensity = 10,
|
radius = 10,
|
||||||
quality = "medium",
|
quality = "medium",
|
||||||
},
|
},
|
||||||
}, createDeps())
|
}, createDeps())
|
||||||
|
|
||||||
luaunit.assertNotNil(renderer.backdropBlur)
|
luaunit.assertNotNil(renderer.backdropBlur)
|
||||||
luaunit.assertEquals(renderer.backdropBlur.intensity, 10)
|
luaunit.assertEquals(renderer.backdropBlur.radius, 10)
|
||||||
luaunit.assertEquals(renderer.backdropBlur.quality, "medium")
|
luaunit.assertEquals(renderer.backdropBlur.quality, "medium")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -923,7 +923,7 @@ function TestRendererEdgeCases:test_blur_with_invalid_intensity()
|
|||||||
id = "test1",
|
id = "test1",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = -10, quality = 5 },
|
contentBlur = { radius = -10, quality = 5 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element1)
|
luaunit.assertNotNil(element1)
|
||||||
|
|
||||||
@@ -932,7 +932,7 @@ function TestRendererEdgeCases:test_blur_with_invalid_intensity()
|
|||||||
id = "test2",
|
id = "test2",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
backdropBlur = { intensity = 200, quality = 5 },
|
backdropBlur = { radius = 200, quality = 5 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element2)
|
luaunit.assertNotNil(element2)
|
||||||
end
|
end
|
||||||
@@ -943,7 +943,7 @@ function TestRendererEdgeCases:test_blur_with_invalid_quality()
|
|||||||
id = "test1",
|
id = "test1",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = 10, quality = 0 },
|
contentBlur = { radius = 10, quality = 0 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element1)
|
luaunit.assertNotNil(element1)
|
||||||
|
|
||||||
@@ -952,7 +952,7 @@ function TestRendererEdgeCases:test_blur_with_invalid_quality()
|
|||||||
id = "test2",
|
id = "test2",
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 100,
|
height = 100,
|
||||||
contentBlur = { intensity = 10, quality = 100 },
|
contentBlur = { radius = 10, quality = 100 },
|
||||||
})
|
})
|
||||||
luaunit.assertNotNil(element2)
|
luaunit.assertNotNil(element2)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user