widget.newtabbar: width passed is too small to fit the tab items inside

Dear Corona-community,

I am trying to change my bottom-menu symbols from separate image files to symbols coming from an imageSheet to reduce memory usage.

Here is my code in a simplified main.lua:

----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- -- Your code here local widget = require("widget") -- Image sheet options and declaration local options = { frames = { { x=0, y=0, width=400, height=400 }, { x=400, y=0, width=400, height=400 }, { x=0, y=400, width=400, height=400 }, { x=400, y=400, width=400, height=400 }, { x=0, y=800, width=400, height=400 }, { x=400, y=800, width=400, height=400 }, { x=0, y=1200, width=400, height=400 }, { x=400, y=1200, width=400, height=400 }, { x=0, y=1600, width=400, height=400 }, { x=400, y=1600, width=400, height=400 }, { x = 0, y = 0, width = 1, height = 1} }, sheetContentWidth = 800, sheetContentHeight = 2000 } local tabBarSheet = graphics.newImageSheet( "menuSheet.png", options ) -- create menu options local tabButtons = { { size = 25, defaultFrame = 1, overFrame = 2, selected = true, onPress = function() print("blub") end }, { size = 25, defaultFrame = 3, overFrame = 4, onPress = function() print("blub") end }, { size = 25, defaultFrame = 5, overFrame = 6, onPress = function() print("blub") end }, { size = 25, defaultFrame = 7, overFrame = 8, onPress = function() print("blub") end }, { size = 25, defaultFrame = 9, overFrame = 10, onPress = function() print("blub") end } } local tabBar = widget.newTabBar( { sheet = tabBarSheet, height = 50, width = display.contentWidth, top = display.actualContentHeight + display.screenOriginY - 50, x = screenOriginX, buttons = tabButtons, tabSelectedLeftFrame = 11, tabSelectedMiddleFrame = 11, tabSelectedRightFrame = 11, tabSelectedFrameWidth = 1, tabSelectedFrameHeight = 1, backgroundFrameWidth = 0, backgroundFrameHeight = 0 } )

Unfortunately, I get the following error:

widget.newTabBar: width passed is too small to fit the tab items inside, you need a width of at least 2000 to fit your tab items inside

I dont get why I need at least 2000 pixels as width. I understand that this comes from adding up the width-values (400px) for each button, but shouldnt the newTabBar just scale that down for me as I specified “size = 25” in my tabButtons table?

I attached a sample project that demonstrates my problem.
 

I still don’t understand why I get the error above, but I found a possible workaround and would like to ask if that’s sensitive in terms of memory-usage for example:

I now have a “menubar.lua” module file that contains my buttons, all of which are in a group to allow showing and hiding the menu:

local globalSpace = require("globals") local composer = require( "composer" ) -- table holding that tabbar local tabBar = {} -- set height for tabbar tabBar.menuHeight = 50 -- initialize buttons tabBar.btn1 = nil tabBar.btn2 = nil tabBar.btn3 = nil tabBar.btn4 = nil tabBar.btn5 = nil -- initialize group for buttons tabBar.buttonGroup = nil -- draw line on top of menu tabBar.topline = nil -- initially enable tapping on symbols tabBar.isEnabled = true function tabBar.createMenu() -- options for imageSheet local options = { frames = { { x=0, y=0, width=400, height=400 }, { x=400, y=0, width=400, height=400 }, { x=0, y=400, width=400, height=400 }, { x=400, y=400, width=400, height=400 }, { x=0, y=800, width=400, height=400 }, { x=400, y=800, width=400, height=400 }, { x=0, y=1200, width=400, height=400 }, { x=400, y=1200, width=400, height=400 }, { x=0, y=1600, width=400, height=400 }, { x=400, y=1600, width=400, height=400 } }, sheetContentWidth = 800, sheetContentHeight = 2000 } -- actual imageSheet with menu-symbols on it local tabBarSheet = graphics.newImageSheet( "menuSheet.png", options ) -- group holding the symbol-Sprites tabBar.buttonGroup = display.newGroup() tabBar.topline = display.newRect(display.contentCenterX, display.screenOriginY + display.actualContentHeight - 50, display.contentWidth, 1) tabBar.topline:setFillColor(0.9) tabBar.buttonGroup:insert(tabBar.topline) -- home-button tabBar.btn1 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="home", start=1, count=2}) tabBar.btn1:setFrame(1) tabBar.btn1.width = 25 tabBar.btn1.height = 25 tabBar.btn1.x = display.contentWidth \* 0.1 tabBar.btn1.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn1:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("dofaver") end ) -- add-button tabBar.btn2 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="add", start=3, count=2}) tabBar.btn2:setFrame(1) tabBar.btn2.width = 25 tabBar.btn2.height = 25 tabBar.btn2.x = display.contentWidth \* 0.3 tabBar.btn2.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn2:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("addFirst") end ) -- faver-button tabBar.btn3 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="faver", start=5, count=2}) tabBar.btn3:setFrame(1) tabBar.btn3.width = 25 tabBar.btn3.height = 25 tabBar.btn3.x = display.contentWidth \* 0.5 tabBar.btn3.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn3:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("myfavers") end ) -- search-button tabBar.btn4 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="search", start=7, count=2}) tabBar.btn4:setFrame(1) tabBar.btn4.width = 25 tabBar.btn4.height = 25 tabBar.btn4.x = display.contentWidth \* 0.7 tabBar.btn4.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn4:addEventListener("tap", function() print("search coming") end ) -- profile-button tabBar.btn5 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="profile", start=9, count=2}) tabBar.btn5:setFrame(1) tabBar.btn5.width = 25 tabBar.btn5.height = 25 tabBar.btn5.x = display.contentWidth \* 0.9 tabBar.btn5.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn5:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("setAccount") end ) -- hide menu after creation and show it only when first scene is loaded tabBar.buttonGroup.isVisible = false end -- function to show menu function tabBar.showMenu() tabBar.buttonGroup.isVisible = true end -- function to hide menu function tabBar.hideMenu() tabBar.buttonGroup.isVisible = false end -- function to disable tabbar function tabBar.preventTapping() tabBar.isEnabled = false end -- function to enable tabbar function tabBar.allowTapping() tabBar.isEnabled = true end return tabBar

So, in my main.lua I can call

local tabbar = require(“menubar”)

tabbar.createMenu()

tabbar.showMenu()

to display the menubar. Since it initially resides outside composer scenes, the menubar is always on top of scene-content. But as some of my scenes show overlays which - of course - should be on top of the tabbar, I need to insert the tabbar into the sceneGroups. To do that, I call

sceneGroup:insert(tabbar.buttonGroup)

in scene:show - phase of every scene. If I understand correctly, the tabbar is now both globally available as well as handed from one sceneGroup to the other so that it is always inserted into the currently active scene.

Does that make sense? Is this memory-wise a good solution?

Would appreciate any help here :slight_smile:

Thanks,

Chris

You are trying to fit 400 pixel wide graphics in a space that’s designed for 25 pixel wide images. You haven’t shared your config.lua, but something tells me you’re not using a 2000 pixel wide content area.

Even if you’re using content scaling, you would have one image sheet with 25 pixel wide images, an @2x version at 50px per image and an @4x version that’s 100px per image. You’re still 4x bigger than the 4x images that would be used.

Rob

Thanks Rob. I am not quite sure whether my config.lua plays a big role here. In my workaround in my second post, I also use 400px wide graphics (it’s the exact same options table) and create the exact same imageSheet as in the original post. And I put them in 25px wide images without any problem using the exact same config.lua. Only difference is that I use display.newSprite instead of a widget.newTabbar.

I can’t really explain this behaviour. Can you? :slight_smile:

In your second example, you don’t appear to be using the tabBar widget, instead, you’re creating your own tabbar. In doing so, you’re creating your own display.newImageRect() objects and explicitly changing the width and height to 25 each.

In your first example, you’re feeding 400 pixel images into the widget and 400 pixel images X 5 buttons is 2000 pixels which is wider than the width of the tabBar.

Your config.lua comes into play two ways. First, your settings your tabBar width to display.contentWidth, which is the value of “width” that you put in config.lua. If you’re using 320 or 800 or some other value, you still can’t fit 2000 pixels of buttons into that space.

Secondly, your config.lua contains code that defines when at @2x and @4x images are used, which you don’t appear to be using.

Rob

Thanks Rob. It is still a little bit confusing. You write:

“In your first example, you’re feeding 400 pixel images into the widget and 400 pixel images X 5 buttons is 2000 pixels which is wider than the width of the tabBar.”


I figured that with the help of:

local tabButtons = {
    {
        size = 25,
        defaultFrame = 1,
        overFrame = 2,
        selected = true,
        onPress = function() print(“blub”) end
    },

I wouldnt feed the widget with 400 pixel images, but scale them down to 25px each. In the docs it states that

size (optional)

Number. Font size (in pixels) for the tab button label. Default is 8.

My understanding is that by writing size = 25, the tabbar should understand that each button is only 25px wide. Looks like I got this wrong I guess…

size (optional)

Number. Font size (in pixels) for the tab button label. Default is 8.

 

Note, this is the “font size”, not the image size.

 

Rob

Ouch, too much coding in progress, didnt see that at all. Argh…

Ok then, thanks for that last hint :slight_smile:

I still don’t understand why I get the error above, but I found a possible workaround and would like to ask if that’s sensitive in terms of memory-usage for example:

I now have a “menubar.lua” module file that contains my buttons, all of which are in a group to allow showing and hiding the menu:

local globalSpace = require("globals") local composer = require( "composer" ) -- table holding that tabbar local tabBar = {} -- set height for tabbar tabBar.menuHeight = 50 -- initialize buttons tabBar.btn1 = nil tabBar.btn2 = nil tabBar.btn3 = nil tabBar.btn4 = nil tabBar.btn5 = nil -- initialize group for buttons tabBar.buttonGroup = nil -- draw line on top of menu tabBar.topline = nil -- initially enable tapping on symbols tabBar.isEnabled = true function tabBar.createMenu() -- options for imageSheet local options = { frames = { { x=0, y=0, width=400, height=400 }, { x=400, y=0, width=400, height=400 }, { x=0, y=400, width=400, height=400 }, { x=400, y=400, width=400, height=400 }, { x=0, y=800, width=400, height=400 }, { x=400, y=800, width=400, height=400 }, { x=0, y=1200, width=400, height=400 }, { x=400, y=1200, width=400, height=400 }, { x=0, y=1600, width=400, height=400 }, { x=400, y=1600, width=400, height=400 } }, sheetContentWidth = 800, sheetContentHeight = 2000 } -- actual imageSheet with menu-symbols on it local tabBarSheet = graphics.newImageSheet( "menuSheet.png", options ) -- group holding the symbol-Sprites tabBar.buttonGroup = display.newGroup() tabBar.topline = display.newRect(display.contentCenterX, display.screenOriginY + display.actualContentHeight - 50, display.contentWidth, 1) tabBar.topline:setFillColor(0.9) tabBar.buttonGroup:insert(tabBar.topline) -- home-button tabBar.btn1 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="home", start=1, count=2}) tabBar.btn1:setFrame(1) tabBar.btn1.width = 25 tabBar.btn1.height = 25 tabBar.btn1.x = display.contentWidth \* 0.1 tabBar.btn1.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn1:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("dofaver") end ) -- add-button tabBar.btn2 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="add", start=3, count=2}) tabBar.btn2:setFrame(1) tabBar.btn2.width = 25 tabBar.btn2.height = 25 tabBar.btn2.x = display.contentWidth \* 0.3 tabBar.btn2.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn2:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("addFirst") end ) -- faver-button tabBar.btn3 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="faver", start=5, count=2}) tabBar.btn3:setFrame(1) tabBar.btn3.width = 25 tabBar.btn3.height = 25 tabBar.btn3.x = display.contentWidth \* 0.5 tabBar.btn3.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn3:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("myfavers") end ) -- search-button tabBar.btn4 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="search", start=7, count=2}) tabBar.btn4:setFrame(1) tabBar.btn4.width = 25 tabBar.btn4.height = 25 tabBar.btn4.x = display.contentWidth \* 0.7 tabBar.btn4.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn4:addEventListener("tap", function() print("search coming") end ) -- profile-button tabBar.btn5 = display.newSprite(tabBar.buttonGroup, tabBarSheet, {name="profile", start=9, count=2}) tabBar.btn5:setFrame(1) tabBar.btn5.width = 25 tabBar.btn5.height = 25 tabBar.btn5.x = display.contentWidth \* 0.9 tabBar.btn5.y = display.screenOriginY + display.actualContentHeight - 25 tabBar.btn5:addEventListener("tap", function() if(tabBar.isEnabled == false) then return true end composer.gotoScene("setAccount") end ) -- hide menu after creation and show it only when first scene is loaded tabBar.buttonGroup.isVisible = false end -- function to show menu function tabBar.showMenu() tabBar.buttonGroup.isVisible = true end -- function to hide menu function tabBar.hideMenu() tabBar.buttonGroup.isVisible = false end -- function to disable tabbar function tabBar.preventTapping() tabBar.isEnabled = false end -- function to enable tabbar function tabBar.allowTapping() tabBar.isEnabled = true end return tabBar

So, in my main.lua I can call

local tabbar = require(“menubar”)

tabbar.createMenu()

tabbar.showMenu()

to display the menubar. Since it initially resides outside composer scenes, the menubar is always on top of scene-content. But as some of my scenes show overlays which - of course - should be on top of the tabbar, I need to insert the tabbar into the sceneGroups. To do that, I call

sceneGroup:insert(tabbar.buttonGroup)

in scene:show - phase of every scene. If I understand correctly, the tabbar is now both globally available as well as handed from one sceneGroup to the other so that it is always inserted into the currently active scene.

Does that make sense? Is this memory-wise a good solution?

Would appreciate any help here :slight_smile:

Thanks,

Chris

You are trying to fit 400 pixel wide graphics in a space that’s designed for 25 pixel wide images. You haven’t shared your config.lua, but something tells me you’re not using a 2000 pixel wide content area.

Even if you’re using content scaling, you would have one image sheet with 25 pixel wide images, an @2x version at 50px per image and an @4x version that’s 100px per image. You’re still 4x bigger than the 4x images that would be used.

Rob

Thanks Rob. I am not quite sure whether my config.lua plays a big role here. In my workaround in my second post, I also use 400px wide graphics (it’s the exact same options table) and create the exact same imageSheet as in the original post. And I put them in 25px wide images without any problem using the exact same config.lua. Only difference is that I use display.newSprite instead of a widget.newTabbar.

I can’t really explain this behaviour. Can you? :slight_smile:

In your second example, you don’t appear to be using the tabBar widget, instead, you’re creating your own tabbar. In doing so, you’re creating your own display.newImageRect() objects and explicitly changing the width and height to 25 each.

In your first example, you’re feeding 400 pixel images into the widget and 400 pixel images X 5 buttons is 2000 pixels which is wider than the width of the tabBar.

Your config.lua comes into play two ways. First, your settings your tabBar width to display.contentWidth, which is the value of “width” that you put in config.lua. If you’re using 320 or 800 or some other value, you still can’t fit 2000 pixels of buttons into that space.

Secondly, your config.lua contains code that defines when at @2x and @4x images are used, which you don’t appear to be using.

Rob

Thanks Rob. It is still a little bit confusing. You write:

“In your first example, you’re feeding 400 pixel images into the widget and 400 pixel images X 5 buttons is 2000 pixels which is wider than the width of the tabBar.”


I figured that with the help of:

local tabButtons = {
    {
        size = 25,
        defaultFrame = 1,
        overFrame = 2,
        selected = true,
        onPress = function() print(“blub”) end
    },

I wouldnt feed the widget with 400 pixel images, but scale them down to 25px each. In the docs it states that

size (optional)

Number. Font size (in pixels) for the tab button label. Default is 8.

My understanding is that by writing size = 25, the tabbar should understand that each button is only 25px wide. Looks like I got this wrong I guess…

size (optional)

Number. Font size (in pixels) for the tab button label. Default is 8.

 

Note, this is the “font size”, not the image size.

 

Rob

Ouch, too much coding in progress, didnt see that at all. Argh…

Ok then, thanks for that last hint :slight_smile: